Десериализация JSON swift 4.2

#json #swift

#json #swift

Вопрос:

Я пытаюсь десериализовать свой JSON с помощью декодируемого протокола, также я использую enum с CodingKey, но это не работает. Мне нужен только вложенный массив (начинающийся с «индикатора») и только несколько полей (все они в структуре). Я перепробовал много разных вариантов, но, к сожалению .. P.S. Также я пытался сделать это без CodingKey. В любом случае ответ был: «Swift.DecodingError.KeyNotFound(CodingKeys(stringValue: «country», intValue: nil)» Если я прочитал это, может быть, причиной является массив (я имею в виду это странное значение intValue)?

JSON

 [  
   {  
      "page":1,
      "pages":2,
      "per_page":50,
      "total":59,
      "sourceid":"2",
      "lastupdated":"2019-03-21"
   },
   [  
      {  
         "indicator":{  
            "id":"IP.PAT.RESD",
            "value":"Patent applications, residents"
         },
         "country":{  
            "id":"SS",
            "value":"South Sudan"
         },
         "countryiso3code":"SSD",
         "date":"2018",
         "value":null,
         "unit":"",
         "obs_status":"",
         "decimal":0
      },
      {  
         "indicator":{  
            "id":"IP.PAT.RESD",
            "value":"Patent applications, residents"
         },
         "country":{  
            "id":"SS",
            "value":"South Sudan"
         },
         "countryiso3code":"SSD",
         "date":"2017",
         "value":null,
         "unit":"",
         "obs_status":"",
         "decimal":0
      },
         ...
   ]
]
 

Мой код

 struct CountryObject: Decodable{
    var country: CountryInfo
    var date: Int
    var value: Int?
    private enum RawValues: String, Decodable{
        case date = "date"
        case vallue = "value"
    }
}
struct CountryInfo: Decodable{//Country names
    var id: String?
    var value: String?
    private enum RawValues: String, Decodable{
        case id = "id"
        case value = "value"
    }
}//
let urlString = "https://api.worldbank.org/v2/country/SS/indicator/IP.PAT.RESD?format=json"
        guard let url = URL(string: urlString) else {return}
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            guard let data = data else {return}
            guard error == nil else {return}
            do{
                let decoder =  JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                let countryObject = try! decoder.decode([CountryObject].self, from: data)
                print(countryObject)
            }catch let error{
                print(error)
            }
        }.resume()
 

Комментарии:

1. Вам необходимо изменить результат ответа, прежде чем вы его расшифруете. Вы пытаетесь декодировать произвольный массив в массив CountryObject , когда вам нужен только 2-й объект в этом произвольном массиве.

2. Это не так просто, как я хочу, но ладно. Спасибо.

Ответ №1:

Создайте корневую структуру и расшифруйте массив с помощью unkeyedContainer

 struct Root : Decodable {

    let info : Info
    let countryObjects : [CountryObject]

    init(from decoder: Decoder) throws {
        var arrayContrainer = try decoder.unkeyedContainer()
        info = try arrayContrainer.decode(Info.self)
        countryObject = try arrayContrainer.decode([CountryObject].self)
    }
}

struct Info : Decodable {
    let page, pages, perPage: Int
    let lastupdated: String
}

struct CountryObject : Decodable {
    let country: CountryInfo
    let date: String
    let value: Int?
}

struct CountryInfo : Decodable { //Country names
    let id: String
    let value: String
}

...

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
    let root = try decoder.decode(Root.self, from: data)
    let countryObjects = root.countryObjects
    print(countryObjects)
} catch { print(error) }
 

(Де) сериализация JSON дважды является неоправданно дорогостоящей.