如何快速解析这种 JSON 格式

Posted

技术标签:

【中文标题】如何快速解析这种 JSON 格式【英文标题】:How to parse this JSON format in swift 【发布时间】:2020-09-29 16:00:11 【问题描述】:

我有这种 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 等等... 你应该在你的问题中添加你想要得到的结果模型,即使你不知道怎么做 我过去回答过一个类似的问题,所以它可能会有所帮助:***.com/a/61854827/968155 责备服务的所有者发送这种伪 CSV 格式。这是非常不切实际的解析。 【参考方案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 我更新了答案以解码点数组。解码响应后,您可以进行任何您需要的搜索。

以上是关于如何快速解析这种 JSON 格式的主要内容,如果未能解决你的问题,请参考以下文章

python json快速解析命令

求助:java中如何使用json-lib解析这种格式的json数据(list中嵌套list)?

教你不编程快速解析 JSON 数据

android json解析成map格式

如何在Retrofit中同时解析多种数据格式

快速解析从 API 以 JSON 形式返回的对象