быстрый синтаксический анализ json, без заголовков для объектов json

#swift #codable

#swift #codable

Вопрос:

Итак, я столкнулся с довольно сложной проблемой для решения. Мой JSON-код имеет довольно странную структуру. Он имеет следующую структуру:

 {
    "title": [
        [
            "Temperature",
            "9 u00b0C (283 u00b0F)",
            "Good"
        ],
        [
            "Visibility",
            "10 KM (6.2 Mi)",
            "Good"
        ]
    ]
}
  

С помощью следующего кода я смог распечатать некоторый простой json-код:

 import UIKit

struct WeatherItem: Decodable {
    let title: String?
    let value: String?
    let condition: String?
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let jsonUrlString = "http://somelinkhere"
        guard let url = URL(string: jsonUrlString) else { return }

        URLSession.shared.dataTask(with: url) { (data, response, err) in
            guard let data = data else { return }

            do {
                let weather = try JSONDecoder().decode(WeatherItem.self, from: data)
                print(weather.title)
            } catch let jsonErr{
                print("error", jsonErr)
            }
        }.resume()
    }
}
  

Но проблема в том, что мой вывод для всех 3 переменных, title, value и condition равен нулю.
Я уверен, что мне нужно изменить код структуры, но я не знаю каким образом.

Как мне перейти к JSON-коду без заголовка?

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

1. Приведенный вами пример JSON кажется немного не таким. Я скопировал и вставил это в jsonformatter.curiousconcept.com и он говорит, что это недопустимо. Итак, если вы можете предоставить допустимый JSON, это помогло бы.

2. @LuisFRamirez json правильный, он просто отсутствует { и }

3. А, ладно. Думаю, не хватало скобок.

4. привет, спасибо за вашу помощь! Я попробовал ваше решение, но оно ничего не напечатало

5. полный json здесь: virtualflight.ddns.net/api/weather.php?icao=ehrd

Ответ №1:

Вам придется написать инициализатор декодирования самостоятельно:

 struct WeatherData: Decodable {
    let title: [WeatherItem]
}

struct WeatherItem: Decodable {
    let title: String?
    let value: String?
    let condition: String?

    public init(from decoder: Decoder) throws {
        // decode the value for WeatherItem as [String]         
        let container = try decoder.singleValueContainer()
        let components = try container.decode([String].self)

        title = components.count > 0 ? components[0] : nil
        value = components.count > 1 ? components[1] : nil
        condition = components.count > 2 ? components[2] : nil
    }
}

let json = """
{
  "title": [
     ["Temperature", "9", "Good"],
     ["Visibility", "10 KM (6.2 Mi)", "Good"]
   ]
}
"""

let jsonData: Data = json.data(using: .utf8)!
let decoder = JSONDecoder()

let decoded = try! decoder.decode(WeatherData.self, from: jsonData)
debugPrint(decoded)
  

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

1. но информация JSON постоянно меняется.

2. @DylanStrijker Структура меняется? Вы не указали это в своем вопросе! Что, вероятно, было бы лучше WeatherItem просто сохранить [String] ? Неясно, каков формат данных и какие операции вы выполняете над ними.

3. Я имел в виду, что информация всегда меняется. Например, слово temperaturen будет таким же, но вторая строка будет изменяться.

4. @DylanStrijker Тогда я не вижу проблемы. Я только что использовал этот JSON в качестве примера того, как анализировать данные. Я думал, это будет очевидно.

5. хорошо, спасибо! Ваш пример работает, но я хотел бы использовать эту ссылку: virtualflight.ddns.net/api/weather.php?icao=ehrd как я могу это сделать с помощью ссылки? Я новичок в синтаксическом анализе JSON

Ответ №2:

Правильный json

 {
    "title": [
        [
            "Temperature",
            " ",
            "Good"
        ],
        [
            "Visibility",
            "10 KM (6.2 Mi)",
            "Good"
        ]
    ]

}
  

 var arr = [WeatherItem]()
do {
     let res = try JSONDecoder().decode([String:[[String]]].self, from: data)
     let content = res["title"]!
     content.forEach {
       if $0.count >= 3 {
         arr.append(WeatherItem(title:$0[0],value:$0[1],condition:$0[2]))
       }
     }
     print(arr)
} catch {
  print(error)
}
  

Обсуждение : ваш корневой объект — это словарь, который содержит 1 ключ с именем title , а его значением является массив из массива строк или, исходя из логики модели, это массив с именем модели, WeatherItem но не структурированный должным образом для него, поэтому, используя это

 let weather = try JSONDecoder().decode(WeatherItem.self, from: data)
  

не будет работать, поскольку текущий json не содержит ключей value и condition

Правильная структура была бы

 [
    {
        "title":"Temperature" ,
        "value":"",
        "condition":"Good"
    },
    {
        "title":"Visibility",
        "title":"10 KM (6.2 Mi)",
        "condition":"Good"
    }
]
  

и это позволит вам сделать

 let weather = try JSONDecoder().decode([WeatherItem].self, from: data)