代码之家  ›  专栏  ›  技术社区  ›  Said-Abdulla Atkaev

使用Codable使用动态编码键解码json?

  •  0
  • Said-Abdulla Atkaev  · 技术社区  · 6 年前

    我需要解码的Json示例。在“text”键中,我们有[String:String]指令,元素的数量是“count”。我应该如何正确地解码它?

    {
    "name": "LoremIpsum",
    "index": "1",
    "text": {
        "text_1": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
        "text_2": "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
        "text_3": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ",
        "text_4": "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
    },
    "count": "4"
    }
    

    我的可编码模型:

    class Text: Codable {
    
    private enum CodingKeys: String, CodingKey {
        case name, index, count, text
    }
    
    public var name: String?
    public var index: Int?
    public var count: Int?
    public var texts: [String]?
    
    init() {
        name = ""
        index = 0
        count = 0
        texts = []
    }
    
    init(name: String,
         index: Int,
         count: Int,
         texts: [String]) {
        self.name = name
        self.index = index
        self.count = count
        self.texts = texts
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        var text = container.nestedContainer(keyedBy: CodingKeys.self, forKey: . text)
    
    }    <---- also why do I need this method? 
    
    required public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
    
        self.name = try container.decode(String.self, forKey: .name)
        self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
        self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
        let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
    
        for i in (1...self.count!) {
            self.texts!.append(try text.decode(String.self, forKey: Text.CodingKeys.init(rawValue: "text_\(i)") ?? .text))
        }
    }
    
    }
    

    我用以下方法对其进行解码:

    if let path = Bundle.main.path(forResource: "en_translation_001", ofType: "json") {
            do {
                let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
                let jsonObj = try JSONDecoder().decode(Text.self, from: data)
                print("jsonData:\(jsonObj)")
            } catch let error {
                print("parse error: \(error.localizedDescription)")
            }
        } else {
            print("Invalid filename/path.")
        }
    

    我的代码有什么问题?这是解码这种动态编码键的好方法吗?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Shehata Gamal    6 年前

    你需要

    struct Root: Codable {
        let name, index,count: String
        let text: [String:String]
    }
    

    let res = try? JSONDecoder().decode(Root.self,from:jsonData)
    
        2
  •  2
  •   Community kfsone    4 年前

    修复JSON

    我想你希望你的索引和计数是数字。所以换掉这个 "index": "1" "count": "4" 用这个 "index": 1 还有这个 "count": 4

    阶级结构

    对于可编码协议,这些编码键和编码函数或所需的初始化都不是必需的。同时将文本属性数据格式更改为[String:String]

    因此,请按如下方式替换您的类:

    class Text: Codable {
    
        public var name: String
        public var index: Int
        public var count: Int
        public var texts: [String: String]
    
        init(name: String, index: Int, count: Int, texts: [String: String]) {
            self.name = name
            self.index = index
            self.count = count
            self.texts = texts
        }
    
    }
    

    解码

    要解码json,请使用上面所写的内容,这是正确的

    let object = try JSONDecoder().decode(Text.self, from: data)
    
        3
  •  2
  •   Rob Napier    6 年前

    您需要的最重要的东西是:

    let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
    texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
    

    allKeys 是有序的(因为它是一个序列化协议)。

    如果只符合以下条件,则不需要encode方法 Decodable 而不是 Codable . 而且,你的大多数期权都不是真正的期权。

    把这些东西放在一起,你会发现:

    class Text: Decodable {
    
        private enum CodingKeys: String, CodingKey {
            case name, index, count, text
        }
    
        public var name: String
        public var index: Int
        public var count: Int
        public var texts: [String]
    
        init(name: String = "",
             index: Int = 0,
             count: Int = 0,
             texts: [String] = []) {
            self.name = name
            self.index = index
            self.count = count
            self.texts = texts
        }
    
        required public init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
    
            self.name = try container.decode(String.self, forKey: .name)
            self.index = Int(try container.decode(String.self, forKey: .index)) ?? 0
            self.count = Int(try container.decode(String.self, forKey: .count)) ?? 0
            let text = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .text)
            texts = try text.allKeys.map { try text.decode(String.self, forKey: $0) }
        }
    
    }
    
    let jsonObj = try JSONDecoder().decode(Text.self, from: data)