如何从不同级别的 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 let
或 if 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 信息提取到一个数据帧中