You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift解码Firebase实时数据库数据失败及相关疑问

Firebase Realtime Database解码问题及控制台显示疑问

问题描述

在Firebase Realtime Database中根据lemma键搜索值时,能打印数据但无法完成解码,报错如下:

keyNotFound(CodingKeys(stringValue: "nouns", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "nouns", intValue: nil) ("nouns").", underlyingError: nil))

同时存在两个控制台显示疑问:

  1. 打印数据时lemma和article的值为何没有引号?
  2. 部分字符显示为\U00e4是什么原因?

控制台输出

# Key: 1278, # Value: {
    accusativePlural = "Die H\U00e4mmer";
    accusativeSingular = "Den Hammer";
    // Why Der is not in String ? 
    article = Der;
    dativePlural = "Den H\U00e4mmer";
    dativeSingular = "Dem Hammer";
    genitivePlural = "Der H\U00e4mmer";
    genitiveSingular = "Des Hammers";
    // Why Hammer is not in String ? 
    lemma = Hammer;
    nominativePlural = "Die H\U00e4mmer";
    nominativeSingular = "Der Hammer";
}

代码实现

RealtimeDatabaseManager

final class RealtimeDatabaseManager {
    static let shared = RealtimeDatabaseManager()
    private let ref = Database.database().reference()
    
    private init() { }
    
    func search(with text: String) {
        print("foo search: \(text)")
        ref.child("nouns")
            .queryOrdered(byChild: "lemma")
            .queryStarting(atValue: text)
            .queryEnding(atValue: text + "\u{f8ff}")
            .observe(.value) { snapshot in
                do {
                    
                    let decoded = try snapshot.data(as: GermanNounsResponse.self)
                    print("# decoded: \(decoded.nouns)")
                } catch {
                    print("# error \(error)")
                }
                if let snapDict = snapshot.value as? [String: Any] {
                    for (key, value) in snapDict {
                        print("# Key: \(key), # Value: \(value)")
                    }
                }
        }
    }
}

响应模型

struct GermanNounsResponse: Decodable {
    let nouns: [GermanResponse]
    
    enum CodingKeys: String, CodingKey {
        case nouns
    }
}

struct GermanResponse: Decodable {
    let lemma: String
    let pos: String
    let article: String
    let nominativSingular: String
    let nominativPlural: String
    let akkusativSingular: String
    let akkusativPlural: String
    let dativSingular: String
    let dativPlural: String
    let genitivSingular: String
    let genitivPlural: String
    
    enum CodingKeys: String, CodingKey {
        case lemma
        case pos
        case article
        case nominativSingular
        case nominativPlural
        case akkusativSingular
        case akkusativPlural
        case dativSingular
        case dativPlural
        case genitivSingular
        case genitivPlural
    }
}

问题解答

1. 解码失败的原因及修复

核心原因

  • 数据结构不匹配:查询返回的是nouns节点下的直接子节点集合(键为节点ID,值为名词对象),但GermanNounsResponse模型期望根结构是包含nouns数组的对象,两者不兼容。
  • 属性名映射错误:数据库使用英文拼写的键名(如accusativeSingular),但模型用了德语拼写(如akkusativSingular),没有做对应映射。
  • 缺失字段报错:模型中声明了pos字段,但数据库返回的数据里没有该键,解码时因缺少必要字段失败。

修复步骤

步骤1:适配数据结构

因为查询返回的是[String: GermanResponse]格式,可直接解码为字典,或修改GermanNounsResponse的解码逻辑:

方法一:直接解码为字典

// 移除GermanNounsResponse,直接解码成字典
let decoded = try snapshot.data(as: [String: GermanResponse].self)
// 转成数组使用
let nounsArray = Array(decoded.values)

方法二:修改GermanNounsResponse适配字典结构

struct GermanNounsResponse: Decodable {
    let nouns: [GermanResponse]
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let nounsDict = try container.decode([String: GermanResponse].self)
        nouns = Array(nounsDict.values)
    }
}
步骤2:修正属性名映射

在GermanResponse的CodingKeys中添加数据库键名与模型属性的映射:

struct GermanResponse: Decodable {
    let lemma: String
    let article: String
    let nominativSingular: String
    let nominativPlural: String
    let akkusativSingular: String
    let akkusativPlural: String
    let dativSingular: String
    let dativPlural: String
    let genitivSingular: String
    let genitivPlural: String
    let pos: String? // 设为可选,因为数据库无此字段
    
    enum CodingKeys: String, CodingKey {
        case lemma, article, pos
        case nominativSingular = "nominativeSingular"
        case nominativPlural = "nominativePlural"
        case akkusativSingular = "accusativeSingular"
        case akkusativPlural = "accusativePlural"
        case dativSingular = "dativeSingular"
        case dativPlural = "dativePlural"
        case genitivSingular = "genitiveSingular"
        case genitivPlural = "genitivePlural"
    }
}
步骤3:处理缺失的pos字段

将模型中的pos设为可选类型(String?),避免因数据库无此字段导致解码失败。

2. 控制台显示疑问解答

疑问1:lemma和article的值没有引号

这是Swift打印[String: Any]的格式规则:纯字母数字的字符串(无空格、特殊字符)会省略引号,带空格的字符串(如"Den Hammer")会保留引号。仅为打印格式问题,实际数据类型仍是字符串,不影响使用。

疑问2:部分字符显示为\U00e4

\U00e4是Unicode转义序列,对应德语变音字符ä。控制台打印非ASCII字符时会自动转义,实际存储和使用时是正常的ä,直接访问字符串属性即可看到正确显示(如print(nominativePlural)会输出Die Hämmer)。


内容的提问来源于stack exchange,提问作者Ufuk Köşker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 14:58:10