【问题标题】:Parse JSON with variable keys使用可变键解析 JSON
【发布时间】:2018-04-16 19:36:14
【问题描述】:

我正在尝试使用包含动态键和动态数量值的货币汇率解析 JSON。输出取决于输入参数,例如基础货币和要比较的几种货币。

JSON 示例:

{
"USD_AFN": 70.129997,
"USD_AUD": 1.284793,
"USD_BDT": 82.889999,
"USD_BRL": 3.418294,
"USD_KHR": 4004.99952
}

,或者:

{
"EUR_CAD": 0.799997
}

另外,我应该能够更改基础货币和要比较的货币,以及更改要比较的货币数量。 我已经尝试过这个answer

处理它的最佳方法是什么?

谢谢

其他信息

所以,我制作了没有初始化器的结构

struct CurrencyRate: Codable { 
var results : [String:Double] 
} 

并试图对其进行解码

 do { let results = try decoder.decode(CurrencyRate.self, from: dataToDecode) print(results) } catch { print("Error") }

我仍然收到错误消息。

最终,我只需要一组货币汇率(值)即可将其填充到表格视图中。

【问题讨论】:

  • 我会保留字典(解码成[String:Double]
  • 我尝试使用这个结构 struct CurrencyRate: Codable { var results : [String:Double] init(results: [String:Double]) { self.results = results }let results = try decoder.decode(CurrencyRate.self, from: dataToDecode) ,但它发现了一个错误。可能我的结构不适合这种情况?
  • 这是整个 JSON 吗?如果是,则代码应该可以工作。您不需要结构中的初始化程序,即使不符合Codable
  • 是的,这是整个 JSON,但它可能包含任意数量的对象和任意键组合 (XXX_XXX)。所以,我在没有初始化器struct CurrencyRate: Codable { var results : [String:Double] } 的情况下创建了结构,并尝试对其进行解码do { let results = try decoder.decode(CurrencyRate.self, from: dataToDecode) print(results) } catch { print("Error") } 我仍然收到错误消息。我做错了什么?
  • 在注释中放置多行代码是个坏主意。如果您喜欢这样做,请将其添加到问题中。您的 JSON 缺少 results 键,这就是它无法解析的原因。目前您的JSON 是一个简单的哈希[String:Double],即使使用JSONSerializer 也可以解码。但是,您的“最佳”问题暴露了(通常的)问题:“您想(用它)做什么?”

标签: json swift codable


【解决方案1】:

经过一些实验,我的 Playground 如下所示:

import Cocoa
import Foundation

let jsonData = """
{
"USD_AFN": 70.129997,
"USD_AUD": 1.284793,
"USD_BDT": 82.889999,
"USD_BRL": 3.418294,
"USD_KHR": 4004.99952
}
""".data(using: .utf8)!

do {
    let obj = try JSONSerialization.jsonObject(with:jsonData, options:[])
    print(obj)          // this is an NSDictionary
    if let dict = obj as? [String:Double] {
        print(dict)     // This is not "just" a cast ... more than I thought
    }
}

struct CurrencyRate: Codable {
    var results : [String:Double]
}

// If you use a "results"-key it _must_ be present in your JSON, but it would allow to add methods
let resultsJson = """
{
  "results" : {
    "USD_AFN": 70.129997,
    "USD_AUD": 1.284793,
    "USD_BDT": 82.889999,
    "USD_BRL": 3.418294,
    "USD_KHR": 4004.99952
    }
}
""".data(using: .utf8)!

do {
    let currencyRate = try JSONDecoder().decode(CurrencyRate.self, from: resultsJson)
    print(currencyRate)
}

// this is probably the easiest solution for just reading it
do {
    let rates = try JSONDecoder().decode([String:Double].self, from:jsonData)
    print(rates)
}

// While you could do the following it does not feel "proper"
typealias CurrencyRatesDict = [String:Double]

extension Dictionary where Key == String, Value == Double {
    func conversionRate(from:String, to:String) -> Double {
        let key = "\(from)_\(to)"
        if let rate = self[key] {
            return rate
        } else {
            return -1.0
        }
    }
}

do {
    let currRates = try JSONDecoder().decode(CurrencyRatesDict.self, from:jsonData)
    print(currRates)
    print(currRates.conversionRate(from:"USD", to:"AUD"))
}

这教会了我一些事情。我不会想到NSDictionary(由JSONSerialization.jsonObject 自动生成并且没有类型)很容易将其转换为[String:Double],但当然它可能会失败,您应该编写一些错误处理来捕获它。

您的CurrencyRate struct 将具有允许轻松扩展的优势。由于字典是structs,因此无法从中派生。正如上一个版本所示,可以将条件扩展添加到Dictionary。但是,这会将您的新函数添加到 any Dictionary 匹配签名,这在许多情况下可能是可以接受的,即使从设计角度来看它“感觉”是错误的。

正如您所见,在 Swift 中有很多方法可以处理这个问题。我建议您使用Codable 协议和附加密钥。很可能您还想对您的对象做“其他事情”。

【讨论】:

    猜你喜欢
    • 2013-05-29
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多