Быстрый синтаксический анализ проблемы json-ответа динамических ключей

#json #swift #parsing #dynamic #key

Вопрос:

Приведенный ниже код анализирует только один объект в массиве, но в ответе json есть два объекта. Я не понимаю, почему этот код анализирует только один объект, а не другой. Я получил нулевое значение, когда ниже кода проанализировал второй объект, имя динамических ключей которого «40».

Структура Json

Это структура json, которую я хочу проанализировать с помощью кодируемого класса.

 {  
    "search_result": "",
    "related_product_price_info": [
            {
                "39": {
                    "price": 1000.0,
                    "discount_percentage": 10.0,
                    "related_product_group_id": 1039,
                    "discounted_price": 900.0
                }
            },
            {
                "40": {
                    "price": 999.0,
                    "discount_percentage": 10.0,
                    "related_product_group_id": 1040,
                    "discounted_price": 899.1
                }
            }
        ]
    }
 

Модели

 struct ProductSearchResult: Codable {
    let searchResult: String?
    let relatedProductPriceInfo: [RelatedProductPriceInfo]?
    enum CodingKeys: String, CodingKey {
        case searchResult = "search_result"
        case relatedProductPriceInfo = "related_product_price_info"
    }
}

struct RelatedProductPriceInfo: Codable {
    var relatedProductPrice: RelatedProductPrice?
    private struct DynamicCodingKeys: CodingKey {

        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }

        var intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
        for key in container.allKeys {
            guard let decodedObject = try? container.decode(RelatedProductPrice.self, forKey: DynamicCodingKeys(stringValue: key.stringValue)!) else{
                continue
            }
            relatedProductPrice = decodedObject
           
        }
    }
}
}
 

Разбор

 let result = try? JSONDecoder().decode(ProductSearchResult.self, from: data)
 

Выход

Я могу проанализировать только один объект (ключ = «39»), но не другой (ключ=»40″)

 ▿ Optional<ProductSearchResult>
  ▿ some : ProductSearchResult
    - searchResult : ""
    ▿ relatedProductPriceInfo : Optional<Array<RelatedProductPriceInfo>>
      ▿ some : 2 elements
        ▿ 0 : RelatedProductPriceInfo
          ▿ relatedProductPrice : Optional<RelatedProductPrice>
            ▿ some : RelatedProductPrice
              - price : 1000
              - discountPercentage : 10
              - relatedProductGroupID : 1039
              - discountedPrice : 900
        ▿ 1 : RelatedProductPriceInfo
          - relatedProductPrice : nil
 

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

1. Измените let relatedProductPriceInfo: [String: RelatedProductPriceInfo] и удалите все лишние вещи, которые у вас есть.

2. Нет, это не работает. я прошел result = nil этим путем. @Йоаким Дэниелсон

3. Да, я немного поторопился там, let relatedProductPriceInfo: [[String: RelatedProductPrice]]

4. Нет, все еще есть то же самое result = nil

5. Для меня это прекрасно работает. Общие рекомендации используйте try вместо try? необязательных свойств и избегайте их, и вы можете получить большую помощь, напечатав любые ошибки с помощью do {...} catch { print(error) }

Ответ №1:

Ваша структура данных не совсем правильная. Тело relatedProductPriceInfo на самом деле представляет собой массив словарей. Если вы правильно сопоставите это, вы сможете значительно упростить задачу, и если вы установите опцию декодирования ключей в декодере, вам не придется объявлять все кодовые клавиши.

Как простое решение (вы должны лучше улавливать ошибки и т. Д.)

 struct ProductSearchResult: Codable {
   let searchResult: String?
   let relatedProductPriceInfo: [[String: PriceDetail]]
}


struct PriceDetail: Codable {
   let price: Double
   let discountPercentage: Double
   let relatedProductGroupId: Double
   let discountedPrice: Double
}
   

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let data = Data(JSON.utf8)
let output = try decoder.decode(ProductSearchResult.self, from: data)

//quick and dirty printing of output
output.relatedProductPriceInfo.forEach{print($0.keys, $0.values)}
 

Это дает выходной сигнал

 ["39"] [__lldb_expr_70.PriceDetail(price: 1000.0, discountPercentage: 10.0, relatedProductGroupId: 1039.0, discountedPrice: 900.0)]
["40"] [__lldb_expr_70.PriceDetail(price: 999.0, discountPercentage: 10.0, relatedProductGroupId: 1040.0, discountedPrice: 899.1)]
 

В реальном мире вам может потребоваться сгладить массив словарей или использовать промежуточную структуру для их переноса, все зависит от вашего варианта использования для различных битов данных.