如何从不同级别的 JSON 对象中提取数据

Posted

技术标签:

【中文标题】如何从不同级别的 JSON 对象中提取数据【英文标题】:How to pull out data from different levels of JSON objects 【发布时间】:2018-04-21 22:34:32 【问题描述】:

所以我有一个看起来像这样的 json 对象。


        geometry =     
            location =         
                lat = "51.5194133";
                lng = "-0.1269566";
            ;
        ;
        id = ad6aaec7b7b0fa2c97a127c24845d76135e760ae;
        "place_id" = ChIJB9OTMDIbdkgRp0JWbQGZsS8;
        reference = "CmRRAAAAiC-ErdlAvz74Drejj2mAAh6Plr46e889a3Uv6CrRXFqNtVatoFsOTarDH0KU8KCkWoN--QGv01RSjLBZblbrAHNPGDVdiXikedid0vKMVM_LQtXstrSQFt4s-Z-Wi-1AEhDJRWc9bdWpKHPPOrt7QGTqGhSJgMPENn_wSGbprGYLv52csv5BtQ";
    

我想知道如何提取不同级别的信息,例如位置对象是几何对象中的一个对象,我想从那里提取纬度我该怎么做?

我可以像这样打印出位置对象:

let setOne = jsonResult["results"]! as! NSArray
            let y = setOne[0] as? [String: AnyObject]
            print(y!)
            print((y!["geometry"]!["location"]!)!["lat"])

但是当我尝试这样做时:

print((y!["geometry"]!["location"]!)!["lat"])

它给了我错误:Type 'Any' has no subscript members

【问题讨论】:

【参考方案1】:

也许最简单的方法是使用JSONDecoder 将您的 JSON 直接解码为结构。

您首先需要定义与您的 JSON 结构相匹配的结构,如下所示:

struct Place: Codable 
    let geometry: Geometry
    let id: String
    let place_id: String
    let reference: String

struct Geometry: Codable 
    let location: Location

struct Location: Codable 
    let lat: String
    let lng: String

现在,假设jsonResult["results"] 实际上是NSArray,您首先需要将其转换为Data,然后使用JSONDecoder 将JSON 解码为结构:

if let data = try? JSONSerialization.data(withJSONObject: jsonResult["results"], options: []) 
    if let places = try? JSONDecoder().decode(Array<Place>.self, from: data) 
        print(places[0].geometry.location.lat) //prints "51.5194133"
    

这种方法的优点是您可以编写更少的代码来进行实际解码。

请注意,如果可能缺少任何 JSON 元素,则应将相应的结构 let 属性声明为可选。例如,如果reference 可能并不总是存在,您可以将其编码为:

let reference: String?

JSON 中必须存在非可选的任何内容,否则解码将失败。这就是为什么你要在解码时使用try?,这样你的应用就不会崩溃。

【讨论】:

我认为Codable首先解析服务结果的正确解决方案。 @MasterDNE 应该更具体地说明他的 JSON 来自源 的样子,而不是在解析之间的中间(可能使用不幸的 JSONSerialization)。您的代码非常适合我们所看到的内容,但如果某个地方有一个 [Place],那么它周围几乎没有很多东西是 Codable 无法处理的。 JSONDecoder.decode 应该用于完全解析服务响应。 这是一个非常公平的观点,希望 OP 会考虑它。我不想过多地假设 OP 如何将数据输入 jsonResult。有可能 OP 对它没有太多控制权。【参考方案2】:

假设你有一个结果数据数组,你不需要使用 NSArray,因为你使用的是 Swift,所以你可以从这里开始。

在 Swift 中你需要指定类型,并且除非你真的需要它,否则尽量不要使用 AnyObject,总是提供类型,这就是错误的意思:

// make sure that results exist 
if let resultArray = jsonResult["results"] as? [Dictionary<String,Any>] 
 // loop through the result array
 for object in resultArray 
   // get geometry from the object in the array
   if let geometry = object["geometry"] as? Dictionary<String,Any>
     // make sure it has location
     if let location = geometry["location"] as? Dictionary<String,Any>

       // now get the lat or lng safely.
       let lat = location["lat"] as? String ?? "0.0" 
       let lng = location["lng"] as? String ?? "0.0"   

     
    
  

注意:切勿使用 force-unwrap !因为如果出现问题或找不到特定对象,您的应用程序将崩溃,请确保至少使用 guard letif let,尽管这是手动解析,您可以查看 Swift 4 new Encoding, Decoding and Serialization in Swift 4。

【讨论】:

【参考方案3】:

考虑到您有一个数据数组作为结果 (jsonResult),开始访问您的数组并确定每个参数的类型。

if let resultArray = jsonResult["results"] as? NSArray 
        for aResult in resultArray 

            //using "if let" simply means, in this case, if "geometry" is indeed a dictionary it will execute the statement/s inside this "if" block.
            if let geometry = aResult["geometry"] as? NSDictionary 
                if let location = geometry["location"] as? NSDictionary 
                    if let latValue = location["lat"] as? String 
                        //This will only be executed if the value is String
                        print(latValue)
                    

                    if let lngValue = location["lng"] as? String 
                        print(lngValue)
                    
                
            
        
    

【讨论】:

以上是关于如何从不同级别的 JSON 对象中提取数据的主要内容,如果未能解决你的问题,请参考以下文章

如何删除 R 中仅出现在一个变量级别且从不出现第二个变量级别的所有参与者?

Spotify API:如何将不同级别的 JSON 信息提取到一个数据帧中

如何使用Python从不存在子根的XML数据中提取数据?

JSON 子级别提取 - 如何? Objective-C

在 sqlite 中使用 json_extract 从父对象和子对象中提取数据

从不完整的视频文件中提取元数据