当前位置: 首页 > 面试题库 >

如果单元素解码失败,Swift JSONDecode解码数组也会失败

潘弘博
2023-03-14
问题内容

在使用Swift4和Codable协议时,我遇到了以下问题-似乎没有办法允许JSONDecoder跳过数组中的元素。例如,我有以下JSON:

[
    {
        "name": "Banana",
        "points": 200,
        "description": "A banana grown in Ecuador."
    },
    {
        "name": "Orange"
    }
]

和一个可 编码的 结构:

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
}

解码此json时

let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)

结果products为空。这是可以预期的,因为JSON中的第二个对象没有"points"键,而pointsGroceryProductstruct中不是可选的。

问题是如何允许JSONDecoder“跳过”无效对象?


问题答案:

一种选择是使用包装器类型,尝试对给定值进行解码。nil如果不成功,则存储:

struct FailableDecodable<Base : Decodable> : Decodable {

    let base: Base?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.base = try? container.decode(Base.self)
    }
}

然后,我们可以解码这些数组,并GroceryProductBase占位符中填写以下内容:

import Foundation

let json = """
[
    {
        "name": "Banana",
        "points": 200,
        "description": "A banana grown in Ecuador."
    },
    {
        "name": "Orange"
    }
]
""".data(using: .utf8)!


struct GroceryProduct : Codable {
    var name: String
    var points: Int
    var description: String?
}

let products = try JSONDecoder()
    .decode([FailableDecodable<GroceryProduct>].self, from: json)
    .compactMap { $0.base } // .flatMap in Swift 4.0

print(products)

// [
//    GroceryProduct(
//      name: "Banana", points: 200,
//      description: Optional("A banana grown in Ecuador.")
//    )
// ]

然后,我们.compactMap { $0.base }用于过滤掉nil元素(那些在解码时引发错误的元素)。

这将创建的中间数组[FailableDecodable<GroceryProduct>],这不应该成为问题;但是,如果您希望避免这种情况,可以始终创建另一个包装器类型,该类型将解码和解包来自无键容器的每个元素:

struct FailableCodableArray<Element : Codable> : Codable {

    var elements: [Element]

    init(from decoder: Decoder) throws {

        var container = try decoder.unkeyedContainer()

        var elements = [Element]()
        if let count = container.count {
            elements.reserveCapacity(count)
        }

        while !container.isAtEnd {
            if let element = try container
                .decode(FailableDecodable<Element>.self).base {

                elements.append(element)
            }
        }

        self.elements = elements
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(elements)
    }
}

然后,您将解码为:

let products = try JSONDecoder()
    .decode(FailableCodableArray<GroceryProduct>.self, from: json)
    .elements

print(products)

// [
//    GroceryProduct(
//      name: "Banana", points: 200,
//      description: Optional("A banana grown in Ecuador.")
//    )
// ]


 类似资料:
  • 我正在尝试与RESTful API接口,该API在post中接受应用程序/x-protobuf对象。 .原型示例对象: 使用请求,我可以将此消息发布到服务器并收到200。 例如 当我以SerializeToString()格式查看此有效负载时,它显示类似于 b'\n\t\n\x03foo\x10\x01' 作为健全性检查,然后我可以创建一个新的消息对象和。将其上的ParseFromString()

  • 我正在编写一个解组器,我正在努力与jaxb进行斗争,以获得一个有效的解决方案。 我想解组从生成的MessageDescription。wsdl文件。wsdl文件中的java类是: 由于没有@XmlRootElement,我用包装器扩展了类,并自己添加了注释 我使用我的“包装器”MessageDescription类来解组 解组过程正在工作,但有一个子项始终设置为null。我不知道在哪里可以修复这个

  • 我收到一个作为当前href的一部分。它是用base64编码的。我尝试使用对其进行解码,但得到以下错误: 在“window”上执行“atob”失败:要解码的字符串编码不正确 当我在代码中复制并粘贴提取的并转到一个在线解码站点时,它会正确解码。你有什么建议吗?

  • 这篇文章是关于前一个问题的解决方案: OptaPlanner-两个规划实体类的求解器配置问题 这是错误:

  • 我试图将我的网页连接到我的Lex机器人使用来自AWS SDK for PHP的postContent。 ‘在“https://runtime.lex.us-east-1.amazonaws.com/bot/xxxx/alias/xxx/user/xxxxxxxx/content”上执行“postContent”时出错; AWS HTTP错误:客户端错误:导致响应:{“消息”:“无效请求:未能解码会

  • 早上好,我试图在我们的代码中找到一个错误,关于当xml没有被格式化时使用JAXB解组XML。我已经做了很多调试和测试,但仍然找不到错误。 xml的解释部分如下所示: 列表最多可包含50.000<代码> 为此,我们有以下代码。解组器的创建被移动到一个接口,以区别我们希望通过jaxb解组的元素,包括子元素(如元素)和那些(如