我有一些JSON,我想用JSONDecoder来解码.问题是,当从服务器发送时,其中一个属性的名称是动态的.

这样地:

{
   "someRandomName": [ [1,2,3], [4,5,6] ],
   "staticName": 12345
}

someRandomName在构建时未知时,我该如何解码?我一直在网上搜寻答案,但仍然没有找到快乐.我真的搞不懂这DecodableCodingKey的工作原理.有些例子有几十行,但这似乎不正确!

EDIT我应该指出,密钥在运行时是已知的,所以也许我可以在解码对象时传递它?

有没有办法连接到协议方法或属性中的一个来启用这种解码?我不介意我是否必须为这个对象编写一个定制的解码器:所有其他JSON都是标准的.

EDIT

好吧,我的理解让我走了这么远:

struct Pair: Decodable {
    var pair: [[Double]]
    var last: Int
    
    private struct CodingKeys: CodingKey {
        
        var stringValue: String
        
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        
        // Use for integer-keyed dictionary
        var intValue: Int?
        init?(intValue: Int) {
            // We are not using this, thus just return nil
            return nil
        }
    }
    
    init(from decoder: Decoder) throws {
        
        // just to stop the compiler moaning
        pair = [[]]
        last = 0
        
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // how do I generate the key for the correspond "pair" property here?
        for key in container.allKeys {
            last = try container.decode(Int.self, forKey: CodingKeys(stringValue: "last")!)
            pair = try container.decode([[Double]].self, forKey: CodingKeys(stringValue: key.stringValue)!)
        }
        
    }
    
}

init() {
    let jsonString = """
{
"last": 123456,
"XBTUSD": [ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0] ]
}
"""
    
    let jsonData = Data(jsonString.utf8)
    
    // this gives: "Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "last", intValue: nil)], debugDescription: "Expected to decode Array<Any> but found a number instead.", underlyingError: nil))"
    let decodedResult = try! JSONDecoder().decode(Pair.self, from: jsonData)

    dump(decodedResult)
}

所以我现在明白了,CodingKey一致性是为序列化数据生成密钥,而不是Swift struct (现在我想这是非常合理的).

那么,我现在如何动态生成pair的 case ,而不是像这样硬编码呢?我知道这与我需要实现的init(from decoder: Decoder)有关,但就我的一生而言,我无法弄清楚它是如何工作的.请帮忙!

EDIT 2

好了,我现在离你很近了.解码似乎与此有关:

struct Pair: Decodable {
    var pair: [[Double]]
    var last: Int
    
    private enum CodingKeys : String, CodingKey {
        case last
    }
    
    private struct DynamicCodingKeys: CodingKey {
        var stringValue: String
        
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        
        // Use for integer-keyed dictionary
        var intValue: Int?
        init?(intValue: Int) {
            // We are not using this, thus just return nil
            return nil
        }
    }
    
    init(from decoder: Decoder) throws {
        
        // just to stop the compiler moaning
        pair = [[]]
        last = 0
        
        let container1 = try decoder.container(keyedBy: CodingKeys.self)
        last = try container1.decode(Int.self, forKey: .last)
        
        let container2 = try decoder.container(keyedBy: DynamicCodingKeys.self)
        for key in container2.allKeys {
            pair = try container2.decode([[Double]].self, forKey: DynamicCodingKeys(stringValue: key.stringValue)!)
        }
        
    }
    
}

这段代码似乎完成了它的工作:判断函数本身的lastpair属性,结果看起来不错;但我在try 解码时出错了:

init() {
    let jsonString = """
{
"last": 123456,
"XBTUSD": [ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0] ]
}
"""
    
    let jsonData = Data(jsonString.utf8)
    
    // Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [DynamicCodingKeys(stringValue: "last", intValue: nil)], debugDescription: "Expected to decode Array<Any> but found a number instead."
    let decodedResult = try! JSONDecoder().decode(Pair.self, from: jsonData)
    
    dump(decodedResult)
}

我离得很近,我能尝到...

推荐答案

如果动态密钥在运行时已知,则可以通过解码器的userInfo字典传递它.

首先创建两个扩展

extension CodingUserInfoKey {
    static let dynamicKey = CodingUserInfoKey(rawValue: "dynamicKey")!
}

extension JSONDecoder {
    convenience init(dynamicKey: String) {
        self.init()
        self.userInfo[.dynamicKey] = dynamicKey
    }
}

在struct中,实现CodingKeys作为struct,以便能够动态创建关键点.

struct Pair : Decodable {
    let last : Int
    let pair : [[Double]]
    
    private struct CodingKeys: CodingKey {
        var intValue: Int?
        var stringValue: String
        init?(stringValue: String) {  self.stringValue = stringValue  }
        init?(intValue: Int) {
            self.stringValue = String(intValue)
            self.intValue = intValue
        }
        static let last = CodingKeys(stringValue: "last")!
        static func makeKey(name: String) -> CodingKeys {
            return CodingKeys(stringValue: name)!
        }
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        guard let dynamicKey = decoder.userInfo[.dynamicKey] as? String else {
            throw DecodingError.dataCorruptedError(forKey: .makeKey(name: "pair"), in: container, debugDescription: "Dynamic key in userInfo is missing")
        }
        last = try container.decode(Int.self, forKey: .last)
        pair = try container.decode([[Double]].self, forKey: .makeKey(name: dynamicKey))
    }
}

现在创建传递已知动态名称的JSONDecoder

let jsonString = """
{
  "last": 123456,
  "XBTUSD": [ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0] ]
}
"""

do {
    let decoder = JSONDecoder(dynamicKey: "XBTUSD")
    let result = try decoder.decode(Pair.self, from: Data(jsonString.utf8))
    print(result)
} catch {
    print(error)
}

Edit:

如果JSON始终只包含两个键,那么这是一种更简单的方法:

struct AnyKey: CodingKey {
    var stringValue: String
    var intValue: Int?
    
    init?(stringValue: String) {  self.stringValue = stringValue  }
    init?(intValue: Int) {
        self.stringValue = String(intValue)
        self.intValue = intValue
    }
}

struct Pair : Decodable {
    let last : Int
    let pair : [[Double]]
}

let jsonString = """
{
  "last": 123456,
  "XBTUSD": [ [1.0, 2.0, 3.0], [4.0, 5.0, 6.0] ]
}
"""

do {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .custom({ codingPath in
        let lastPath = codingPath.last!
        if lastPath.stringValue == "last" { return lastPath }
        return AnyKey(stringValue: "pair")!
    })
    let result = try decoder.decode(Pair.self, from: Data(jsonString.utf8))
    print(result)
} catch {
    print(error)
}

Json相关问答推荐

Vega-Lite时钟(使用Vega-Lite中的计时器)

如何在PowerShell中访问嵌套的JSON字段

将PNG图像保存为Python中的JSON文件

在Jolt中将具有嵌套元素的对象数组转换为单独的对象列表

当并非所有子对象都有 Select 器字段时 Select

将带有::text[]的JSON数组转换未按预期工作

如何在 Apache NiFi 中使用 JoltTransformJson 删除流文件或具有空字段的整个对象?

Groovy JsonBuilder 在for循环中添加数组元素

使用 serde 和 csv crates 将嵌套的 json 对象序列化为 csv

将文本转换为 python 列表

如何判断 Json 对象中是否存在键并获取其值

避免 KeyError 的默认字典键

.NET 对象最灵活的序列化是什么,但实现起来很简单?

如何从Typescript 中的json响应中获取日期对象

在 Postgres 中向 JSON 对象添加元素

C#扁平化json struct

Golang struct 的 XML 和 JSON 标签?

你如何在 Arrays of Arrays 上 OPENJSON

SCRIPT5009:JSON未定义

从调试器获取 IntelliJ Idea 中的 JSON 对象