在 Swift 中解码具有不同结构的相同 JSON 的更好方法?

问题描述 投票:0回答:1

我正在处理一个需要修改我们的iOS模型结构的请求,修改是将

EncodingOption
参数从
EditingOptions 
结构移动到
OutputOptions
类。

配置的层次结构

AppConfig {
profile: ProfileSetting
}

ProfileSetting {
editSetting:EditingOptions
outputSetting: OutputOptions
}

以前的数据结构:

class EditingOptions: NSObject, Codable {
//..other parameters
}

struct OutputOptions: Codable {
var barcodeEncoding: EncodingOption = .utf8
//..other parameters
}

修改后的数据结构:

class EditSetting: NSObject, Codable {
var encodingOption: EncodingOption
//..other parameters
}

struct OutputSetting: Codable {
//..other parameters
}

修改后的结构与之前的配置文件不兼容,出现 JSON 错误

keyNotFound
:

keyNotFound(CodingKeys(stringValue: "encodingOption", intValue: nil), 
Swift.DecodingError.Context(codingPath: [
CodingKeys(stringValue: "profileManage", intValue: nil), CodingKeys(stringValue: "deviceName",
 intValue: nil), CodingKeys(stringValue: "profiles", intValue: nil), 
_JSONKey(stringValue: "Index 0", intValue: 0), 
CodingKeys(stringValue: "outputOptions", intValue: nil)
],
 debugDescription: "No value associated with key CodingKeys(stringValue: \"encodingOption\", intValue: nil) (\"encodingOption\").", underlyingError: nil))

ChatGPT 的解决方案是为

init(from decoder: Decoder)
类添加
ProfileSetting
方法:

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

        //...other parameters
        
        if let editingOptions = try? container.decode(EditingOptions.self, forKey: .editingOptions) {
            self.editingOptions = editingOptions
        } else {
            self.editingOptions = EditingOptions()
        }
    }

barcodeEncoding
是一个默认设置的隐藏参数,尚未公开发布。

该解决方案确实解决了

keyNotFound
错误,但是,我不确定该解决方案是否是解决问题的 RIGHT 方法。

json swift codable
1个回答
0
投票

如果我理解正确,你的问题可以简化为:

 let json = """
 {
    "a": { "keyA": "Value for KeyA" },
    "b": { "keyB": "Value for KeyB",
            "otherKey": "My value To Pass to a now "}
 }
"""

其中

otherKey
是你的
encodingOption
。它存在于
B
上,但现在您希望它将它传递给
A

所以旧代码是:

func oldCode(with json: String) {
    struct OldParent: Codable {
        let a: OldA
        let b: OldB
    }
    
    class OldA: NSObject, Codable {
        let keyA: String
    }
    
    struct OldB: Codable {
        let keyB: String
        let otherKey: String
    }
    
    let decoder = JSONDecoder()
    
    do {
        let parent = try decoder.decode(OldParent.self, from: Data(json.utf8))
        print(parent)
        print("--")
    } catch {
        print("Error: \(error)")
    }
}

它按设计工作,您的 iOS 模型反映了 API 模型。

现在你想修改你的iOS模型,但是API不会改变。

一个可能的解决方案是将

otherKey
保留在
B
中,并在
Parent
的解码中(它知道
A
B
),将该值传递给
A
:

func newCode(with json: String) {
    struct NewParent: Codable {
        let a: NewA
        let b: NewB
        
        init(from decoder: any Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            self.a = try container.decode(NewA.self, forKey: .a)
            self.b = try container.decode(NewB.self, forKey: .b)
            self.a.otherKey = self.b.otherKey //Pass the value
        }
    }
    
    class NewA: NSObject, Codable {
        let keyA: String
        var otherKey: String = "defaultValue"
        
        enum CodingKeys: CodingKey {
            case keyA //We skip here otherKey to tell the decoder to decode only keyA which is present in the JSON
        }
    }
    
    struct NewB: Codable {
        let keyB: String
        let otherKey: String //Only because it's in JSON
    }
    
    let decoder = JSONDecoder()
    
    do {
        let parent = try decoder.decode(NewParent.self, from: Data(json.utf8))
        print(parent)
        print("--")
    } catch {
        print("Error: \(error)")
    }
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.