Home > Software design >  Swift parsing issue of json response of dynamic keys
Swift parsing issue of json response of dynamic keys

Time:09-21

The below code only parse one object in array, but in json response there are two object. I don't understand why this code only parse one object not another. I got nil value when below code parse second object which dynamic keys name is "40".

Json structure

This is json structure which i want to parse by using Codable class.

{  
    "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
                }
            }
        ]
    }

Models

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
           
        }
    }
}
}

Parsing

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

Output

I only able to parse one object (key = "39") but not another (key="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

CodePudding user response:

Your data structure isn't quite right. The body of relatedProductPriceInfo is actually an array of dictionaries. If you map this correctly you can simplify things considerably, and if you set the key decoding option on the decoder you can avoid having to declare all the CodingKeys.

As a bare bones solution (you should trap errors etc better)

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)}

This gives an output off

["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)]

In the real world you might then want to flatten the array of dictionaries, or use an intermediate struct to wrap them, all depending on your use case for the various bits of data.

  • Related