1

我正在尝试将我的数据解码为结构。这是我的 JSON 数据结构之一的示例:

{
    "name": "abc",
    "owner": "bcd",
    "fallbackLanguage": "tr",
    "localizedValues": {
        "en": {
            "description": "Lorem Ipsum Dolor Sit Amet"
        },
        "de": {
            "description": "Sed Do Eiusmod Tempor Incididunt"
        },
        "tr": {
            "description": "Consectetur Adipisicing Elit"
        }
    }
}

这个 JSON 对象的结构是:

struct X {
  let name: String
  let owner: String
  let fallbackLanguage: String

  let description: String
}

解码nameowner并且fallbackLanguage不是问题并且已经完成。这是当前CodingKeyinit(from:)

struct CodingKeys: CodingKey {
    var stringValue: String
    init?(stringValue: String) {
      self.stringValue = stringValue
    }

    var intValue: Int?
    init?(intValue: Int) {
      self.intValue = intValue
      self.stringValue = "\(intValue)"

    }

    static func makeKey(name: String) -> CodingKeys {
      return CodingKeys.init(stringValue: name)!
    }
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    owner = try container.decode(String.self, forKey: CodingKeys.makeKey(name: "owner"))
    name = try container.decode(String.self, forKey: CodingKeys.makeKey(name: "name"))
    fallbackLanguage = try container.decode(String.self, forKey: CodingKeys.makeKey(name: "fallbackLanguage"))

    // FIXME: decode localizedValues into `description`
}

问题在于解码description,因为它保存在多级字典中,并且它的值会因设备区域设置而改变。

在此示例中,如果设备语言环境不是ende或者tr,它将回退到,tr因为 fallbackLanguage 是tr

任何帮助和建议都会很棒。谢谢你。

注意:我通过inamiy包含了这个要点来编码/解码字典和数组。

4

5 回答 5

2

您可以使用此扩展进行编码和Array<Any>解码Dictionary<String: Any>

斯威夫特 4:

struct X: Decodable {

    let name: String?
    let owner: String?
    let fallbackLanguage: String?
    let localizedValues: Dictionary<String, Any>?

    enum CodingKeys: String, CodingKey {
        case name, owner, fallbackLanguage, localizedValues
    }
}
于 2018-04-13T12:52:49.637 回答
2

我建议创建一个结构localizedValues并将值解码为字典。然后获取当前fallbackLanguage并从字典中获取相应的描述。CodingKeys不需要自定义和初始化程序。

let jsonString = """
{    "name": "abc",
    "owner": "bcd",
    "fallbackLanguage": "tr",
    "localizedValues": {
        "en": { "description": "Lorem Ipsum Dolor Sit Amet"},
        "de": { "description": "Sed Do Eiusmod Tempor Incididunt"},
        "tr": { "description": "Consectetur Adipisicing Elit"}
    }
}
"""

struct X : Decodable {
    let name: String
    let owner: String
    let fallbackLanguage: String

    let localizedValues: [String:LocalizedValue]
}

struct LocalizedValue : Decodable {
    let description : String
}

let data = Data(jsonString.utf8)
do {
    let result = try JSONDecoder().decode(X.self, from: data)
    let fallBackLanguage = result.fallbackLanguage
    let fallBackLanguageDescription = result.localizedValues[fallBackLanguage]?.description
    print(fallBackLanguageDescription)

} catch { print(error)}
于 2018-04-13T11:25:31.427 回答
2

感谢@AndyObusek,我了解到有nestedContainers.

这是我的问题的最终解决方案:

    // get localizedValues as nested container
    let localizedValues = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: CodingKeys.makeKey(name: "localizedValues")!)

    var localizations: KeyedDecodingContainer<CodingKeys>

    // if device locale identifier exists in the localizedValues container as a key get it as nested container, else get for fallbackLanguage.

    if localizedValues.contains(CodingKeysmakeKey(name: Locale.current.identifier)!) {
      localizations = try localizedValues.nestedContainer(keyedBy: CodingKeys.self, forKey: CodingKeys.makeKey(name: Locale.current.identifier))
    } else {
      localizations = try localizedValues.nestedContainer(keyedBy: CodingKeys.self, forKey: CodingKeys.makeKey(name: fallbackLanguage))
    }

    // set description
    description = try localizations.decode(String.self, forKey: CodingKeys.makeKey(name: "description"))

编辑:

我的数据是Cloud Firestore数据,我发现alickbass创建了一个很棒的 POD,用于为 Firebase 编码和解码数据,名为CodableFirebase,如果您使用 Firebase 数据库或 Cloud Firestore,这是最简单和最好的解决方案

于 2018-04-13T11:33:01.417 回答
0

如果你的日程安排不紧,试试这个库:Swifty Json

它有一些内置方法可以抽象您的需求,例如将嵌套字典 Json 对象转换为 NSDictionary,您可以在其中提取值并对它们应用控制流以围绕它们构建逻辑。

于 2018-04-13T20:57:37.600 回答
0

使用您的 json,您可以创建以下结构

struct LocalizedJSONObject: Codable {
    let name, owner, fallbackLanguage: String?
    let localizedValues: LocalizedValues?
}

struct LocalizedValues: Codable {
    let en, de, tr: De?
}

struct De: Codable {
    let description: String?
}

// MARK: Convenience initializers

extension LocalizedJSONObject {
    init(data: Data) throws {
        self = try JSONDecoder().decode(LocalizedJSONObject.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func jsonData() throws -> Data {
        return try JSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

extension LocalizedValues {
    init(data: Data) throws {
        self = try JSONDecoder().decode(LocalizedValues.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func jsonData() throws -> Data {
        return try JSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

extension De {
    init(data: Data) throws {
        self = try JSONDecoder().decode(De.self, from: data)
    }

    init(_ json: String, using encoding: String.Encoding = .utf8) throws {
        guard let data = json.data(using: encoding) else {
            throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
        }
        try self.init(data: data)
    }

    init(fromURL url: URL) throws {
        try self.init(data: try Data(contentsOf: url))
    }

    func jsonData() throws -> Data {
        return try JSONEncoder().encode(self)
    }

    func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
        return String(data: try self.jsonData(), encoding: encoding)
    }
}

并像这样使用

let localizedJSONObject = try LocalizedJSONObject(json)
于 2018-04-13T12:14:33.333 回答