当前位置: 首页 > 知识库问答 >
问题:

使用Swift 4中的JSONDecoder,丢失的键可以使用默认值,而不必是可选属性吗?

封俊艾
2023-03-14

Swift 4 添加了新的 Codable 协议。当我使用JSONDecoder时,它似乎要求我的Codable类的所有非可选属性在JSON中具有键,否则会引发错误。

使我的类的每个属性都是可选的似乎是一个不必要的麻烦,因为我真正想要的是使用json中的值或默认值。(我不希望属性为零。

有没有办法做到这一点?

class MyCodable: Codable {
    var name: String = "Default Appleseed"
}

func load(input: String) {
    do {
        if let data = input.data(using: .utf8) {
            let result = try JSONDecoder().decode(MyCodable.self, from: data)
            print("name: \(result.name)")
        }
    } catch  {
        print("error: \(error)")
        // `Error message: "Key not found when expecting non-optional type
        // String for coding key \"name\""`
    }
}

let goodInput = "{\"name\": \"Jonny Appleseed\" }"
let badInput = "{}"
load(input: goodInput) // works, `name` is Jonny Applessed
load(input: badInput) // breaks, `name` required since property is non-optional

共有3个答案

司空坚
2023-03-14

我更喜欢的方法是使用所谓的DTO-数据搬迁对象。它是一个结构,符合可编码并表示所需的对象。

struct MyClassDTO: Codable {
    let items: [String]?
    let otherVar: Int?
}

然后,您只需使用该DTO初始化要在应用程序中使用的对象即可。

 class MyClass {
    let items: [String]
    var otherVar = 3
    init(_ dto: MyClassDTO) {
        items = dto.items ?? [String]()
        otherVar = dto.otherVar ?? 3
    }

    var dto: MyClassDTO {
        return MyClassDTO(items: items, otherVar: otherVar)
    }
}

这种方法也很好,因为您可以根据自己的意愿重命名和更改最终对象。这很清楚,并且比手动解码需要更少的代码。此外,通过这种方法,您可以将网络层与其他应用程序分开。

文寒
2023-03-14

如果未找到JSON键,可以使用默认为所需值的计算属性。

class MyCodable: Codable {
    var name: String { return _name ?? "Default Appleseed" }
    var age: Int?

    // this is the property that gets actually decoded/encoded
    private var _name: String?

    enum CodingKeys: String, CodingKey {
        case _name = "name"
        case age
    }
}

如果您想要readwrite属性,也可以实现setter:

var name: String {
    get { _name ?? "Default Appleseed" }
    set { _name = newValue }
}

这会增加一点额外的冗长,因为您需要声明另一个属性,并且需要添加CodingKeys枚举(如果还没有)。优点是您不需要编写自定义解码/编码代码,这在某些时候可能会变得乏味。

请注意,此解决方案仅在JSON键的值包含字符串或不存在时才有效。如果JSON可能具有另一种形式的值(例如,它是int),那么您可以尝试此解决方案

蔡理
2023-03-14

您可以在您的类型中实现init(来自解码器:解码器)方法,而不是使用默认实现:

class MyCodable: Codable {
    var name: String = "Default Appleseed"

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let name = try container.decodeIfPresent(String.self, forKey: .name) {
            self.name = name
        }
    }
}

您还可以使< code>name成为常量属性(如果您愿意):

class MyCodable: Codable {
    let name: String

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let name = try container.decodeIfPresent(String.self, forKey: .name) {
            self.name = name
        } else {
            self.name = "Default Appleseed"
        }
    }
}

required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Default Appleseed"
}

重新发表评论:使用自定义扩展程序

extension KeyedDecodingContainer {
    func decodeWrapper<T>(key: K, defaultValue: T) throws -> T
        where T : Decodable {
        return try decodeIfPresent(T.self, forKey: key) ?? defaultValue
    }
}

您可以将init方法实现为

required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.name = try container.decodeWrapper(key: .name, defaultValue: "Default Appleseed")
}

但这并不比

    self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Default Appleseed"
 类似资料:
  • 问题内容: Swift 4添加了新协议。当我使用它时,似乎要求类的所有非可选属性在JSON中具有键,否则会引发错误。 使类的每个属性都是可选的似乎是不必要的麻烦,因为我真正想要的是使用json中的值或默认值。(我不希望该属性为零。) 有没有办法做到这一点? 问题答案: 我更喜欢的方法是使用所谓的DTO-数据传输对象。它是一个结构,符合Codable并表示所需的对象。 然后,您只需使用该DTO初始化

  • 在使用Jackson反序列化Json时,当Json文件中缺少属性时,是否有方法指定默认值

  • 问题内容: 我想将类属性用作类方法的参数之一的默认值。但是,此构造引发了一个异常,而我不明白为什么: 为什么会失败,并且有办法做到这一点? 问题答案: 这是因为,根据文档: 执行功能定义时将评估默认参数值。 这意味着在定义函数时,表达式将被计算一次 当定义了时,的定义是不完整的,因为它仍在解析中,因此您还不能参考。解决该问题的一种方法是将一个特殊的唯一值传递给,例如:

  • 指定:这就是表的样子。 希望得到的结果是由Hibernate为我构建的SQL查询:

  • 问题内容: 我将 .properties 文件中的属性注入到 @Value 注释的字段中。但是,此属性提供敏感的凭据,因此我将它们从存储库中删除。我仍然希望在某些情况下想要运行项目并且没有带有默认值将设置为字段的凭据的.properties文件。 问题 即使我将默认值设置为字段本身,当.properties文件不存在时,也会出现异常: 这是带注释的字段: 我希望在这种情况下只设置普通的字符串“ s

  • 问题内容: 我正在使用JavaScriptSerializer序列化一些实体对象。 问题是,许多公共属性都包含空值或默认值。有什么方法可以使JavaScriptSerializer排除具有null或默认值的属性? 我希望得到的JSON不再那么冗长。 问题答案: 对我有用的解决方案: 序列化的类和属性将如下装饰: IsRequired是关键项。 实际的序列化可以使用DataContractJsonS