JSON 解析,嵌套 JSON 结构的问题
Posted
技术标签:
【中文标题】JSON 解析,嵌套 JSON 结构的问题【英文标题】:JSON Parsing, issue with Nested JSON structure 【发布时间】:2018-06-13 05:22:13 【问题描述】:假设我们有这个URL,我正在使用CodableAlamofire 来获取和解析 JSON 响应。
对于上述 URL 响应,我创建了以下 Codable 类。
struct CoinList: Codable
let raw: Raw
let display: Display
enum CodingKeys: String, CodingKey
case raw = "RAW"
case display = "DISPLAY"
struct Display: Codable
let usd: [String: Usd]
enum CodingKeys: String, CodingKey
case usd = "USD"
struct Raw: Codable
let usd: [String: Usd]
enum CodingKeys: String, CodingKey
case usd = "USD"
struct Usd: Codable
let type, market, fromsymbol, tosymbol: String
let flags: String
let price: Double
let lastupdate: Int
let lastvolume, lastvolumeto: Double
let lasttradeid: String
let volumeday, volumedayto, volume24Hour, volume24Hourto: Double
let openday, highday, lowday, open24Hour: Double
let high24Hour, low24Hour: Double
let lastmarket: String
let change24Hour, changepct24Hour, changeday, changepctday: Double
let supply, mktcap, totalvolume24H, totalvolume24Hto: Double
enum CodingKeys: String, CodingKey
case type = "TYPE"
case market = "MARKET"
case fromsymbol = "FROMSYMBOL"
case tosymbol = "TOSYMBOL"
case flags = "FLAGS"
case price = "PRICE"
case lastupdate = "LASTUPDATE"
case lastvolume = "LASTVOLUME"
case lastvolumeto = "LASTVOLUMETO"
case lasttradeid = "LASTTRADEID"
case volumeday = "VOLUMEDAY"
case volumedayto = "VOLUMEDAYTO"
case volume24Hour = "VOLUME24HOUR"
case volume24Hourto = "VOLUME24HOURTO"
case openday = "OPENDAY"
case highday = "HIGHDAY"
case lowday = "LOWDAY"
case open24Hour = "OPEN24HOUR"
case high24Hour = "HIGH24HOUR"
case low24Hour = "LOW24HOUR"
case lastmarket = "LASTMARKET"
case change24Hour = "CHANGE24HOUR"
case changepct24Hour = "CHANGEPCT24HOUR"
case changeday = "CHANGEDAY"
case changepctday = "CHANGEPCTDAY"
case supply = "SUPPLY"
case mktcap = "MKTCAP"
case totalvolume24H = "TOTALVOLUME24H"
case totalvolume24Hto = "TOTALVOLUME24HTO"
init(from decoder: Decoder) throws
let values = try decoder.container(keyedBy: CodingKeys.self)
type = try values.decode(String.self, forKey: .type)
market = try values.decode(String.self, forKey: .market)
fromsymbol = try values.decode(String.self, forKey: .fromsymbol)
tosymbol = try values.decode(String.self, forKey: .tosymbol)
flags = try values.decode(String.self, forKey: .flags)
price = try values.decode(Double.self, forKey: .price)
lastvolume = try values.decode(Double.self, forKey: .lastvolume)
lastvolumeto = try values.decode(Double.self, forKey: .lastvolumeto)
lastupdate = try values.decode(Int.self, forKey: .lastupdate)
if let value = try? values.decode(Int.self, forKey: .lasttradeid)
lasttradeid = String(value)
else
lasttradeid = try values.decode(String.self, forKey: .lasttradeid)
volumeday = try values.decode(Double.self, forKey: .volumeday)
volumedayto = try values.decode(Double.self, forKey: .volumedayto)
volume24Hour = try values.decode(Double.self, forKey: .volume24Hour)
volume24Hourto = try values.decode(Double.self, forKey: .volume24Hourto)
openday = try values.decode(Double.self, forKey: .openday)
highday = try values.decode(Double.self, forKey: .highday)
lowday = try values.decode(Double.self, forKey: .lowday)
open24Hour = try values.decode(Double.self, forKey: .open24Hour)
high24Hour = try values.decode(Double.self, forKey: .high24Hour)
low24Hour = try values.decode(Double.self, forKey: .low24Hour)
lastmarket = try values.decode(String.self, forKey: .lastmarket)
change24Hour = try values.decode(Double.self, forKey: .change24Hour)
changepct24Hour = try values.decode(Double.self, forKey: .changepct24Hour)
changeday = try values.decode(Double.self, forKey: .changeday)
changepctday = try values.decode(Double.self, forKey: .changepctday)
supply = try values.decode(Double.self, forKey: .supply)
mktcap = try values.decode(Double.self, forKey: .mktcap)
totalvolume24H = try values.decode(Double.self, forKey: .totalvolume24H)
totalvolume24Hto = try values.decode(Double.self, forKey: .totalvolume24Hto)
成功响应后,我无法解析 JSON,我研究了很多关于使用 Swift Codable 进行嵌套 JSON 解析但仍然无法成功。
请帮助我用嵌套的 JSON 结构解析上述 JSON 响应,例如 Display 和 Raw 对象具有 Usd 的所有属性。
我认为我正在做一些小错误。
任何帮助将不胜感激。
更新
我已经为响应创建了 JSON 文件并对其进行解析,
if let path = Bundle.main.path(forResource: "test", ofType: "json")
do
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let result = try JSONDecoder().decode(CoinList.self, from: data)
print(result)
catch
print(error.localizedDescription)
错误是这样的,
JSON结构是这样的,
请找到我的 web api 调用,
Alamofire.request(TickerRouter.PriceMultiFull(params: params))
.validate(statusCode: 200..<300)
.responseString responseData in
let data = responseData.value?.replacingOccurrences(of: "\\/", with: "/").data(using: .utf8)
if responseData.error == nil
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom( AnyKey(stringValue: $0.last!.stringValue.lowercased())!)
decoder.dateDecodingStrategy = .secondsSince1970
let result = try? decoder.decode(CoinList.self, from: data!)
success(result!)
else
let msg = "Something went wrong. Please try again later"
failure(msg)
这里的结果为零,它正在使用本地 json 文件。 :(
【问题讨论】:
您可以节省大量时间编写样板代码,只需使用自定义KeyDecodingStrategy
将您的密钥变为大写即可
你到底有什么问题?
@rmaddy 我已经为以下响应创建了 json 文件,请找到我解析 json 文件的更新答案,错误是附有问题的屏幕截图。请找到它。
结构体的第二行和第三行看起来不正确。
@ElTomato 你能详细说明一下吗?
【参考方案1】:
乍一看,您会注意到 USD
字典中 raw
和 display
的成员类型有很大不同,因此两者的单个结构不起作用。
根对象是(String
键是 BTH
和 XRP
符号)
struct CoinList: Codable
let raw: [String: Raw]
let display: [String: Display]
Raw
和 Display
结构包含 usd
键和相应的结构
struct Raw: Codable
let usd: USDRaw
struct Display: Codable
let usd: USDDisplay
USDRaw
和 USDDisplay
结构包含所有数据,USDRaw
中的lastupdate
将被解码为Date
struct USDRaw: Codable
let type, market, flags, fromsymbol, tosymbol: String
let price : Double
let lastupdate: Date
let lastvolume, lastvolumeto: Double
let lasttradeid: String
let volumeday, volumedayto, volume24hour, volume24hourto: Double
let openday, highday, lowday, open24hour: Double
let high24hour, low24hour: Double
let lastmarket: String
let change24hour, changepct24hour, changeday, changepctday: Double
let supply, mktcap, totalvolume24h, totalvolume24hto: Double
struct USDDisplay: Codable
let fromsymbol, tosymbol, market, price, lastupdate: String
let lastvolume, lastvolumeto, lasttradeid, volumeday, volumedayto, volume24hour, volume24hourto : String
let openday, highday, lowday, open24hour, high24hour, low24hour, lastmarket: String
let change24hour, changepct24hour, changeday, changepctday: String
let supply, mktcap, totalvolume24h, totalvolume24hto: String
要摆脱指定所有 CodingKeys 并使键小写创建一个辅助结构(从documentation 窃取)
struct AnyKey: CodingKey
var stringValue: String
var intValue: Int?
init?(stringValue: String)
self.stringValue = stringValue
self.intValue = nil
init?(intValue: Int)
self.stringValue = String(intValue)
self.intValue = intValue
将自定义的keyDecodingStrategy
和合适的dateDecodingStrategy
传递给解码器
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom( AnyKey(stringValue: $0.last!.stringValue.lowercased())!)
decoder.dateDecodingStrategy = .secondsSince1970
let coinList = try decoder.decode(CoinList.self, from: data)
print(coinList)
【讨论】:
非常感谢,您能否告诉如何克服以下错误“”预期解码字符串但找到了一个数字。“在 lasttradeid? .json 但不是来自服务器的响应 在给定的 URL 中,所有lasttradeid
值都是 String
。但如果不幸发生这种情况,您必须添加巨大的 init(from decoder
方法来处理这些情况。
你能告诉我为什么它不能与服务器响应一起工作吗?
我不熟悉 Alamofire。删除反斜杠的行真的有必要吗?我会直接尝试responseData
。
如果我删除该行,同样的问题:(以上是关于JSON 解析,嵌套 JSON 结构的问题的主要内容,如果未能解决你的问题,请参考以下文章