首页 > 解决方案 > 解码具有动态密钥的 JSON

问题描述

我的回复如下所示JSON。问题是我有散列的动态密钥。我在Decodable为这个响应创建一个结构时很迷失。

我尝试了下面的代码,但由于planName最近引入了数据不匹配而失败。

struct ProductDescriptionResponse {
    let disclaimersHtml: String?
    let additionalProperties: [String: ProductDescription]?
}

struct ProductDescription {
    var disclaimersHtml: String?
    var hasPlanDetailsV2: Bool?
    let planDescription: String
    let serviceDescriptions: [ServiceDescriptions]
}

struct ServiceDescriptions {
    let name: String
    let subTitle: String?
    let icon: String?
    let description: String?
    let upgradeText: String?
    let featureDisclaimer: String?
}

func productDescriptions() -> Observable<ProductDescriptionResponse> {

        return APIClient.sharedInstance.rx.response(apiURL: APIURL.productDescription, requestType: .get, httpBody: nil, header: .auth).map({ (responseData) -> ProductDescriptionResponse in

            var parsedData = try JSONSerialization.jsonObject(with: responseData) as? [String:Any]

            // Remove disclaimersHtml key from responseData & hold it to pass
            // in ProductDescriptionResponse constructor during return.
            let disclaimersHtml = parsedData?.removeValue(forKey: "disclaimersHtml") as? String
            // Product descriptions.
            var productDescriptions: [String: ProductDescription]? = nil

            if let parsedData = parsedData {

                // Json data without disclaimersHtml.
                let jsonData = try JSONSerialization.data(withJSONObject: parsedData, options: .prettyPrinted)

                productDescriptions =  try JSONDecoder().decode([String: ProductDescription].self, from: jsonData)
            }

            // ProductDescriptionResponse with disclaimersHtml & productDescriptions.
            return ProductDescriptionResponse(disclaimersHtml: disclaimersHtml,
                                              additionalProperties: productDescriptions)
        })
    }

JSON响应:

{
    "disclaimersHtml": "",
    "planName": "",
    “abc456753234”: {
        "planDescription": "",
        "hasPlanDetailsV2": false,
        "serviceDescriptions": [
            {
                "name": "",
                "subTitle": "",
                "icon": "",
                "hasTile": "",
                "tileTitle": "",
                "description": "",
                "featureDisclaimer": "",
                "upgradeText": ""
            },
            {
                "name": "",
                "subTitle": "",
                "icon": "",
                "hasTile": "",
                "tileTitle": "",
                "description": "",
                "featureDisclaimer": "",
                "upgradeText": ""
            }
        ]
    },
    “xyz123456789”: {
        "planDescription": "",
        "hasPlanDetailsV2": true,
        "serviceDescriptions": [
            {
                "name": "",
                "subTitle": "",
                "icon": "",
                "hasTile": "",
                "tileTitle": "",
                "description": "",
                "featureDisclaimer": "",
                "upgradeText": ""
            }
        ]
    }
}

如果我在下面这样做,它会起作用。但不想像这样继续硬编码:

let _ = parsedData?.removeValue(forKey: "planName") as? String

这种类型的 JSON 是否有更好的工作方式?我不想硬编码并不断剥离值来获取ProductDescriptionResponse,因为将来可以添加此字段。

标签: iosjsonswiftswift4.2decodable

解决方案


我解决的方法是遍历(key, value)序列化的 JSON。

仅当匹配时才检查value类型Dictionary<AnyHashable,Any>和解码,否则忽略。

func productDescriptions() -> Observable<ProductDescriptionResponse> {

        return APIClient.sharedInstance.rx.response(memberAPIURL: MemberAPIURL.productDescription, requestType: .get, httpBody: nil, header: .auth).map({ (responseData) -> ProductDescriptionResponse in

            var productDescriptionResponse = ProductDescriptionResponse(disclaimersHtml: nil, additionalProperties: nil)

            var additionalParams: [String: ProductDescription] = [:]

                do {

                    productDescriptionResponse  =  try JSONDecoder().decode(ProductDescriptionResponse.self, from: responseData)

                    if let jsonObject = try JSONSerialization.jsonObject(with: responseData, options: .mutableLeaves) as? [String : Any] {

                        for (key,value) in jsonObject {
                            if value is Dictionary<AnyHashable,Any> {
                                let jsonData = try JSONSerialization.data(withJSONObject: value, options: JSONSerialization.WritingOptions.prettyPrinted)
                                let productDescription =  try JSONDecoder().decode(ProductDescription.self, from: jsonData)
                                additionalParams[key] = productDescription
                            }
                        }
                        productDescriptionResponse.additionalProperties = additionalParams

                    }

                } catch {
                    // handle error
                }

            return productDescriptionResponse
        })
    }

推荐阅读