【问题标题】:Attempting to parse dynamic JSON in iOS尝试在 iOS 中解析动态 JSON
【发布时间】:2019-02-19 21:35:01
【问题描述】:

我制作了以下 JSON 示例块。任何以字母结尾的值都是动态的。

{
    "groupName": {
        "groupA": {
            "fields": {
                "fieldA": "valueA",
                "fieldB": "valueB"
            },
            "letters": {
                "letterA: "A"
            }
        },
        "groupB": {
            "fields": {
                "fieldC": "valueC",
                "fieldD": "valueD"
            },
            "letters": {
                "letterB: "B"
            }
        }
    }
}

我的目标是使用Decodable,以便我可以将这些数据读入我定义的structs。

下面是我当前的工作,包含在一个游乐场文件中,我正在使用它来尝试解决这个问题:

import Foundation

let jsonString = "{\"groupName\":{\"groupA\":{\"fields\":{\"fieldA\":\"valueA\",\"fieldB\":\"valueB\"},\"letters\":{\"letterA:\"A\"}},\"groupB\":{\"fields\":{\"fieldC\":\"valueC\",\"fieldD\":\"valueD\"},\"letters\":{\"letterB:\"B\"}}}}"

struct CustomCodingKeys: CodingKey {
    var intValue: Int?
    var stringValue: String

    init?(intValue: Int) { self.intValue = intValue; self.stringValue = "\(intValue)" }
    init?(stringValue: String) { self.stringValue = stringValue }

    static let field = CustomCodingKeys.make(key: "field")

    static func make(key: String) -> CustomCodingKeys {
        return CustomCodingKeys(stringValue: key)!
    }
}

// Values
struct Field {
    let field: String
    let value: String
}

struct Letter: Decodable {
    let title: String
    let letter: String
}

// Value holders
struct FieldData: Decodable {
    var fields: [Field]

    init(from decoder: Decoder) throws {
        self.fields = [Field]()
        let container = try decoder.container(keyedBy: CustomCodingKeys.self)
        for key in container.allKeys {
            print("processing field: \(key.stringValue)")
            let dynamicKey = CustomCodingKeys.make(key: key.stringValue)
            let value = try container.decode(String.self, forKey: dynamicKey)
            let field = Field(field: key.stringValue,
                              value: value)
            fields.append(field)
        }
    }
}

struct LetterData: Decodable {
    var letters: [Letter]

    init(from decoder: Decoder) throws {
        self.letters = [Letter]()
        let container = try decoder.container(keyedBy: CustomCodingKeys.self)
        for key in container.allKeys {
            print("processing letter: \(key.stringValue)")
            let dynamicKey = CustomCodingKeys.make(key: key.stringValue)
            let value = try container.decode(String.self, forKey: dynamicKey)
            let letter = Letter(title: key.stringValue,
                                letter: value)
            letters.append(letter)
        }
    }
}

// Containers
struct Group: Decodable {
    var name: String!
    var groups: [GroupData]

    init(from decoder: Decoder) throws {
        self.groups = [GroupData]()
        let container = try decoder.container(keyedBy: CustomCodingKeys.self)
        for key in container.allKeys {
            print("processing section: \(key.stringValue)")
            let group = try container.decode(GroupData.self,
                                             forKey: key)
            groups.append(group)
        }
    }
}

struct GroupData: Decodable {
    var fieldData: FieldData
    var letterData: LetterData

    enum CodingKeys: String, CodingKey {
        case fieldData = "fields"
        case letterData = "letters"
    }
}

struct GroupList: Decodable {
    struct GroupName: Decodable {
        var name: String!
        var groups: [Group]

        init(from decoder: Decoder) throws {
            self.groups = [Group]()

            let container = try decoder.container(keyedBy: CustomCodingKeys.self)
            for key in container.allKeys {
                let name = key.stringValue
                self.name = name
                print("processing group: \(String(describing: self.name))")
                var group = try container.decode(Group.self,
                                                 forKey: key)
                group.name = name
                groups.append(group)
            }
        }
    }

    let groupName: GroupName
}

let decoder = JSONDecoder()
if let data = jsonString.data(using: .utf8),
    let groupList = try? decoder.decode(GroupList.self,
                                        from: data) {
    print("group list created")
}

在我的GroupData 结构中,我可以删除变量,然后实现init(from decoder: Decoder) throws,当配置了正确的查找(FieldData 和 LetterData 初始化)时,可以识别正确的对。但是,它不会填充正确的值结构。

【问题讨论】:

  • 老实说,我建议您将“动态”键移动到数据中并使用列表而不是对象。它将使您的解析变得更加容易,并且您将获得比简单的 [String:Any] 更多的结构,这是一场铸造噩梦。您正在尝试做与“主流”不同的事情,这通常是一件好事 (TM),但在这种情况下,您给自己制造的麻烦多于其价值。

标签: ios json swift codable decodable


【解决方案1】:

您在解码Group 时有一个小错误。您倾向于解码Group 内的组的所有密钥,并且您还进一步传递解码GroupData,它本身具有“字段”和“字母”。在Group 中使用单值容器应该没问题。

这是您的Group 的外观,

struct Group: Decodable {
    var name: String!
    var groups: GroupData

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        groups = try container.decode(GroupData.self)
    }
}

注意,你的json本身不正确,我已经格式化了,应该是这样的,

let jsonString = "{\"groupName\":{\"groupA\":{\"fields\":{\"fieldA\":\"valueA\",\"fieldB\":\"valueB\"},\"letters\":{\"letterA\":\"A\"}},\"groupB\":{\"fields\":{\"fieldC\":\"valueC\",\"fieldD\":\"valueD\"},\"letters\":{\"letterB\":\"B\"}}}}"

【讨论】:

  • 谢谢@sandeep。 JSON 只是我在制作示例代码时犯了一个错误,但真正的 JSON 是有效的,我应该抓住它!
【解决方案2】:

我使用这个https://app.quicktype.io/ 来帮助我转换它们。 我检查了你的输出,它是错误的:

"letters": {
                "letterA: "A"
            }

正确的: "letterA": "A"

在字母B 中出现相同的一个。

正确检查你的输出:

{
    "groupName": {
        "groupA": {
            "fields": {
                "fieldA": "valueA",
                "fieldB": "valueB"
            },
            "letters": {
                "letterA": "A"
            }
        },
        "groupB": {
            "fields": {
                "fieldC": "valueC",
                "fieldD": "valueD"
            },
            "letters": {
                "letterB": "B"
            }
        }
    }
}

【讨论】:

  • 谢谢@marcos。这是我错误地重新创建生产 JSON 的错误。
猜你喜欢
  • 2012-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-27
  • 1970-01-01
  • 1970-01-01
  • 2020-04-17
  • 1970-01-01
相关资源
最近更新 更多