【问题标题】:How to fix? Expected to decode Dictionary<String, Any> but found a string/data instead怎么修?期望解码 Dictionary<String, Any> 但发现了一个字符串/数据
【发布时间】:2022-11-23 04:52:20
【问题描述】:

这里有什么问题?或者我应该如何解码,我不会使用 JSONSerialize。

let jsonData = try! Data(contentsOf: urls[0])
let decoder = JSONDecoder()
let d = try decoder.decode([String: JSON].self, from: jsonData)

文件内容是一个简单的 JSON:

{"name": "fff", "price": 10}

还有我的JSON代码:

public enum JSON: Decodable {
    case string(String)
    case number(Float)
    case object([String:JSON])
    case array([JSON])
    case bool(Bool)
}

【问题讨论】:

  • 如果文件内容很简单你为什么解码这么复杂?考虑到你必须提炼后来的情况不必要地昂贵。如果你有一个通用的方法,就 Swift 而言,这不是通用的.

标签: json swift


【解决方案1】:

您需要添加一个自定义 init(from:),您可以在其中尝试解码为每个可能的枚举案例,直到成功或抛出错误

这是处理其中三种情况的简短版本

struct EnumDecoderError: Error {}

public init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    if let string = try? container.decode(String.self) {
        self = JSON.string(string)
    } else if let number = try? container.decode(Float.self) {
        self = JSON.number(number)
    } else if let array = try? container.decode([JSON].self) {
        self = JSON.array(array)
    } else {
        throw EnumDecoderError()
    }
}

正如@LeoDabus 在 cmets 中提到的,我们可以捕获类型不匹配错误(并直接抛出任何其他错误),或者如果没有解码工作,则在最后抛出错误。 (又是精简版)

public init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()

    do {
        let string = try container.decode(String.self)
        self = JSON.string(string)
    } catch DecodingError.typeMismatch {
        do {
            let number = try container.decode(Float.self)
            self = JSON.number(number)
        } catch DecodingError.typeMismatch {
            do {
                let array = try container.decode([JSON].self)
                self = JSON.array(array)
            } catch DecodingError.typeMismatch {
                throw DecodingError.typeMismatch(JSON.self, .init(codingPath: decoder.codingPath, debugDescription: "Data type is not supported"))
            }
        }
    }
}

【讨论】:

  • 我认为捕获错误而不是丢弃它会更好。
  • 您指的是什么错误,只是在最后一次尝试中使用 try 而不是 try?
  • 对于他们所有人。您只需要捕获类型不匹配错误。检查这个post
  • 您可以简单地赶上最后一次尝试 AFFAIR。
  • 它在我的测试中工作正常并且没有捕获它给出了一个有点混乱的错误消息,“预期解码 Array<Any> 但发现......”
【解决方案2】:

首先,您不需要在 JSON 枚举中维护数据类型来解析您的响应。

如果您将对象与从本地维护的 API 或 JSON 文件接收的响应结构相匹配,则 JSONDecoder 将能够使用适当的数据类型进行解析

以你的 json 文件为例:

{“名称”:“fff”,“价格”:10}

解析此结构的推荐方法如下

  • 根据您的需要创建结构或类。为此,我将使用一个结构

    struct Product: Decodable {
      var name: String?
      var price: Int?
    

    }

    我已经标记了两个可选变量,以防万一在 JSON 响应中不存在该字段时处理失败。

  • 解析结构

通过创建解码器实例并设置 Product.Self 来解析对象,使用在上一步中创建的产品结构

let decoder = JSONDecoder()
let productObject = try decoder.decode(Product.self, from: jsonData)

如果您在 JSON 响应中有一组具有相同结构的对象,请使用以下方法:

let productObjects = try decoder.decode([Product].self, from: jsonData)

只需在产品对象周围包含 []

【讨论】:

  • 请清理你的代码,struct 是小写的 s 并且 Product 需要符合 Decodable 并且属性声明之间没有逗号。
  • 更新答案!感谢您的强调
  • 另外,我看不出这个解决方案如何处理属于枚举的数组或字典属性?
【解决方案3】:

您需要将其解码为结构

private struct MyData: Codable {
    var name: String?
    var price: Int?
}

...
let jsonData = try! Data(contentsOf: urls[0])
let d = try JSONDecoder().decode(MyData.self, from: jsonData)
...

【讨论】:

  • Namig 你的结构 Data 绝对不是一个好主意。
  • @LeoDabus 你是对的。改为MyData
  • JSON 始终是一个字符串。 Data init contentsOf 绝不能用于下载远程资源。顺便说一句,你应该解码 MyData 而不是 Data
  • 我看不出这个解决方案如何处理作为枚举一部分的数组或字典属性?
  • @JoakimDanielson 我已经从您的示例 JSON 创建了结构。您将需要根据数组(或其他类型)即将到来的 JSON 键修改结构
猜你喜欢
  • 2018-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-23
相关资源
最近更新 更多