【问题标题】:How to parse this JSON format in swift如何快速解析这种 JSON 格式
【发布时间】:2023-03-05 17:40:01
【问题描述】:

我有这种 JSON 格式:

{
  "version":"7.0.19",
  "fields": ["ID","pm","age","pm_0","pm_1","pm_2","pm_3","pm_4","pm_5","pm_6","conf","pm1","pm_10","p1","p2","p3","p4","p5","p6","Humidity","Temperature","Pressure","Elevation","Type","Label","Lat","Lon","Icon","isOwner","Flags","Voc","Ozone1","Adc","CH"],
  "data":[[20,0.0,1,0.0,0.0,0.0,0.0,0.0,0.0,0.0,97,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,null,null,null,1413,0,"Oakdale",40.603077,-111.83612,0,0,0,null,null,0.01,1]],
  "count":11880
}

但我不知道如何使用 Codable 协议来解析 json 响应。

这将是我想要的模型。

struct Point: Codable {
    let pm2: String?
    let latitude, longitude: Double?
    let temp: String?
    let iD: String?
    enum CodingKeys: String, CodingKey {
        case pm2 = "pm", temp = "Temperature", iD = "ID", latitude = "Lat", longitude = "Lon"
    }
}

这是 json 的 URL

https://webbfoot.com/dataa.json

【问题讨论】:

  • 所以,这个想法是"data"中的值顺序是由"fields"中的字段顺序定义的?
  • 是的。例如..第一行 I.D. = 20,下午 = 0.0 等等...
  • 你应该在你的问题中添加你想要得到的结果模型,即使你不知道怎么做
  • 我过去回答过一个类似的问题,所以它可能会有所帮助:stackoverflow.com/a/61854827/968155
  • 责备服务的所有者发送这种伪 CSV 格式。这是非常不切实际的解析。

标签: arrays json swift codable


【解决方案1】:

你可以使用Codable来解析这个:

struct Response: Decodable {

   let version: String
   let fields: [String]
   let data: [[QuantumValue?]]
   let count: Int

}
enter code here

enum QuantumValue: Decodable {

case float(Float), string(String)

init(from decoder: Decoder) throws {
    if let int = try? decoder.singleValueContainer().decode(Float.self) {
        self = .float(float)
        return
    }

    if let string = try? decoder.singleValueContainer().decode(String.self) {
        self = .string(string)
        return
    }

    throw QuantumError.missingValue
}

enum QuantumError:Error {
    case missingValue
}
}

QuantumValue 将处理 FloatString? 将处理 null 部分。

【讨论】:

  • 这不起作用,因为"data" 包含IntStringnull
  • 这行得通,但您应该首先向 OP 澄清生成的模型应该是什么。编辑:查看他的最新编辑
【解决方案2】:

这个比较棘手,需要手动解码。原则是定义您希望解码的字段和对象的属性之间的映射,然后根据属性的类型(StringDouble 等...),尝试解码数据.

第二,既然你有一个点数组,你需要某种容器对象来保存这个数组,例如:

struct Points {
    var data: [Point] = []
}

首先,您的某些模型属性与数据中的类型不匹配,例如iDString,但数据有 Int。为简单起见,我将重新定义您的模型以匹配数据

struct Point {
    var pm2: Int? = nil
    var latitude: Double? = nil
    var longitude: Double? = nil
    var temp: Int? = nil
    var iD: Int? = nil
}

现在,为父容器Points编写手动解码器:

extension Points: Decodable {   
    static let mapping: [String: PartialKeyPath<Point>] = [
        "pm":          \Point.pm2,
        "Lat":         \Point.latitude,
        "Lon":         \Point.longitude,
        "Temperature": \Point.temp,
        "ID":          \Point.iD
    ]

    enum CodingKeys: CodingKey { case fields, data }
    private struct Dummy: Decodable {} // see below why

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

        let fields = try container.decode([String].self, forKey: .fields)
        var data = try container.nestedUnkeyedContainer(forKey: .data)

        while !data.isAtEnd {
            var row = try data.nestedUnkeyedContainer()

            var point = Point()          
            for field in fields {

                let keyPath = Points.mapping[field]
                switch keyPath {
                case let kp as WritableKeyPath<Point, String?>:
                    point[keyPath: kp] = try row.decodeIfPresent(String.self)
                case let kp as WritableKeyPath<Point, Int?>:
                    point[keyPath: kp] = try row.decodeIfPresent(Int.self)
                case let kp as WritableKeyPath<Point, Double?>:
                    point[keyPath: kp] = try row.decodeIfPresent(Double.self)
                default:
                    // this is a hack to skip this value
                    let _ = try? row.decode(Dummy.self)
                }
            }
            self.data.append(point)
        }
    }
}

一旦你有了它,你就可以像这样解码 JSON:

let points = try JSONDecoder().decode(Points.self, from: jsonData)
let firstPoint = points.data[0] 

【讨论】:

  • 我已将 URL 添加到问题中。你能告诉我这在你上面的代码中是如何工作的吗?谢谢
  • 因此,您在 URL 中所拥有的内容与您在问题中所拥有的内容并不完全一样。在 URL 中,JSON 包含data 的多行(内部数组),而您问题中的 JSON 有一个内部行。大概,您想要解码Points 的数组?
  • 是的.. 我在最初的问题中只显示了一行,但我真正想做的是根据其与用户 lat long 的 lat long 接近度找到特定行的 ID
  • 我更新了答案以解码点数组。解码响应后,您可以进行任何您需要的搜索。
猜你喜欢
  • 2016-12-06
  • 2018-03-01
  • 2021-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多