使用映射

时间:2018-05-06 09:51:32

标签: json swift swift4

我使用动态字符串键解码JSON,如下所示:

{
"dynamickey1":
    {"infos1": "blabla","infos2": 21},
"dynamickey2":
    {"infos1": "blabla","infos2": 12},
... }

它们按我的意愿排序,因为我在URL中放了一个字符串列表。但是当它使用(编辑:我把完整的代码)下面的代码解码字典时,我丢失了订单:

struct Devise {
    let nom : String
    let EUR: Float
    let ETH: Float
}

struct DataPrix {
    let devises: [Devise]
    struct DataPrixCodingKeys: CodingKey {
        let stringValue : String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue : Int? {return nil}
        init?(intValue: Int) {return nil}
    }

    enum DeviseCodingKeys: String, CodingKey {
        case ETH
        case EUR
    }
}

let decoder = JSONDecoder()

extension DataPrix: Decodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy : DataPrixCodingKeys.self)
        self.devises = try (container.allKeys).map { key in
            let devisesContainer = try container.nestedContainer(keyedBy: DeviseCodingKeys.self, forKey: key)
            let deviseName = key.stringValue
            let EUR  = try devisesContainer.decode(Float.self, forKey: .EUR)
            let ETH  = try devisesContainer.decode(Float.self, forKey: .ETH)
            return Devise(nom: deviseName, EUR: EUR, ETH: ETH)
        }
    }
}

那么可以按字母顺序对它们进行排序,或者至少保持我在初始列表中的顺序吗?

2 个答案:

答案 0 :(得分:2)

这更可能是container类型的问题。您似乎正在使用NSDictionary,其allKeys类型为AnyAny不是Comparable

最简单的解决方案是字典转换为[String: Any]。然后,您可以直接使用container.keys.sorted(),其中<是默认比较器。

let jsonString = """
{
  "dynamickey1": {"infos1": "blabla","infos2": 21},
  "dynamickey2": {"infos1": "blabla","infos2": 12}
}
"""

let jsonData = jsonString.data(using: .utf8)!
if let container = (try? JSONSerialization.jsonObject(with: jsonData, options: [])) as? [String: Any] {
    let sortedKeys = container.keys.sorted()
    print(sortedKeys)
}

或者,切换到使用Codable协议并安全地解码JSON类型:

let jsonString = """
{
  "dynamickey1": {"infos1": "blabla","infos2": 21},
  "dynamickey2": {"infos1": "blabla","infos2": 12}
}
"""

let jsonData = jsonString.data(using: .utf8)!

struct Info: Codable {
    let infos1: String
    let infos2: Int
}

let parsed = try? JSONDecoder().decode([String: Info].self, from: jsonData)

if let container = parsed {
    print(container.keys.sorted())
}

编辑更新问题的解决方案:

let jsonString = """
{"XPR": {"EUR":0.6866,"ETH":0.001088}}
"""

let jsonData = jsonString.data(using: .utf8)!

enum Currency: String, CodingKey, Comparable {
    case XPR
    case EUR
    case ETH

    public static func < (lhs: Currency, rhs: Currency) -> Bool {
        return lhs.rawValue < rhs.rawValue
    }
}

struct Devise: Decodable {
    let name: String
    let rates: [Currency: Double]

    public init(from decoder: Decoder) throws {
        name = decoder.codingPath.last?.stringValue ?? ""
        let container = try decoder.container(keyedBy: Currency.self)

        var rates: [Currency: Double] = [:]
        try container.allKeys.forEach {
            rates[$0] = try container.decode(Double.self, forKey: $0)
        }
        self.rates = rates
    }
}

struct DataPrix: Decodable {
    let devises: [Devise]

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Currency.self)
        devises = try container.allKeys.sorted().map {
            return try container.decode(Devise.self, forKey: $0)
        }
    }
}

let decoder = JSONDecoder()
do {
  let exchangeRates = try decoder.decode(DataPrix.self, from: jsonData)
  print(exchangeRates)
} catch {
  print(error)
}

但是,如果您的密钥真的是动态,那么您无法避免使用字典,最好只使用它:

let decoder = JSONDecoder()
do {
  let exchangeRates = try decoder.decode([String: [String: Double]].self, from: jsonData)
  print(exchangeRates)
} catch {
  print(error)
}

答案 1 :(得分:1)

您可以这样排序:

let dictionary  = ["key1":"test","key0":"","key5":""]
dictionary.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending }.map { key in
    // code
} 

因此,对于您的代码,您可以使用像这样

  let keys = dic.keys.sorted { $0.localizedCaseInsensitiveCompare($1) == ComparisonResult.orderedAscending }
    print( keys.map { (key) -> [String : Devise]? in
        guard let value = dic[key] else {return nil}
        return [key : value]
        }.compactMap({$0}))