JSON 数组到属性

问题描述 投票:0回答:1

我正在尝试映射以下 JSON

{
    "items": [
        {
            "id": 4,
            "name": "Caffè",
            "is_active": true,
            "position": 5,
            "include_in_menu": true,
            "custom_attributes": [
                {
                    "attribute_code": "meta_title",
                    "value": "Caffè: scopri la linea completa online"
                },
                {
                    "attribute_code": "meta_description",
                    "value": "Scopri il caffè Hausbrandt nello shop ufficiale"
                }
            ]
        },
        {
            "id": 11,
            "name": "Tè",
            "is_active": true,
            "position": 15,
            "include_in_menu": true,
            "custom_attributes": [
                {
                    "attribute_code": "meta_title",
                    "value": "Acquista le Migliori Selezioni di Tè Online"
                },
                {
                    "attribute_code": "meta_description",
                    "value": "Scopri i Tè più pregiati sullo Shop Online"
                },
                {
                    "attribute_code": "thumbnail",
                    "value": "/shop/media/catalog/category/te01.jpg"
                }
            ]
        },
        {
            "id": 17,
            "name": "Tazzine",
            "is_active": true,
            "position": 14,
            "include_in_menu": true,
            "custom_attributes": [
                {
                    "attribute_code": "meta_title",
                    "value": "Tazze e Tazzine Caffè: scopri le Collezioni"
                },
                {
                    "attribute_code": "meta_description",
                    "value": "Acquista online le Tazze e Tazzine da Caffè Hausbrandt"
                },
                {
                    "attribute_code": "thumbnail",
                    "value": "/shop/media/catalog/category/tazzine_2.jpg"
                }
            ]
        }
    ]
}

在这样的结构中:

struct ShopCategory: Decodable {

    let id: Int
    let name: String
    let position: Int
    let isActive: Bool
    let isIncluded: Bool
    let thumbnail: String?
    let metaTitle: String?
    let metaDescription: String?
}

我是这样实现的

struct ShopCategory: Decodable {

    let id: Int
    let name: String
    let position: Int
    let isActive: Bool
    let isIncluded: Bool
    let thumbnail: String?
    let metaTitle: String?
    let metaDescription: String?

}

extension ShopCategory {

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case position
        case isActive = "is_active"
        case isIncluded = "include_in_menu"
        case attributes = "custom_attributes"
    }

    init(from decoder: any Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try values.decode(Int.self, forKey: .id)
        self.name = try values.decode(String.self, forKey: .name)
        self.position = try values.decode(Int.self, forKey: .position)
        self.isActive = try values.decode(Bool.self, forKey: .isActive)
        self.isIncluded = try values.decode(Bool.self, forKey: .isIncluded)
        let attributes = try values.decodeIfPresent([AttributeCode].self, forKey: .attributes)
        self.thumbnail = attributes?.first(where: { $0.code == "thumbnail" })?.value
        self.metaTitle = attributes?.first(where: { $0.code == "meta_title" })?.value
        self.metaDescription = attributes?.first(where: { $0.code == "meta_description" })?.value

    }

}

extension ShopCategory: Hashable { }

private extension ShopCategory {

    struct AttributeCode: Decodable {

        let value: String
        let code: String

        enum CodingKeys: String, CodingKey {
            case code = "attribute_code"
            case value
        }

    }

}

它有效,但它不太喜欢我。我想避免搜索

AttributeCode
数组。我想解码类似字典的类型,然后通过键访问属性。

但我不知道怎么办。

swift decodable
1个回答
2
投票

如果你只是想避免使用

first
进行搜索,你可以将数组转换为字典,如下所示:

let attributeArray = try values.decodeIfPresent([AttributeCode].self, forKey: .attributes) ?? []
// assuming the keys are unique...
let attributes = Dictionary(uniqueKeysWithValues: attributeArray.map { ($0.code, $0.value) })
self.thumbnail = attributes["thumbnail"]
self.metaTitle = attributes["meta_title"]
self.metaDescription = attributes["meta_description"]

如果你想完全避免使用数组,你可以将 JSON 数组解码为这样的类型:

struct AttributeDictionary: Decodable {
    let attributes: [String: String]
    
    subscript(key: String) -> String? {
        get { attributes[key] }
    }
    
    enum CodingKeys: String, CodingKey {
        case code = "attribute_code"
        case value
    }
    
    init(from decoder: any Decoder) throws {
        // JSON arrays are decoded using an unkeyed container
        var unkeyedContainer = try decoder.unkeyedContainer()
        var dict = [String: String]()
        while !unkeyedContainer.isAtEnd {
            // go through the array and put each attribute into dict
            let attributeContainer = try unkeyedContainer.nestedContainer(keyedBy: CodingKeys.self)
            let code = try attributeContainer.decode(String.self, forKey: .code)
            let value = try attributeContainer.decode(String.self, forKey: .value)
            dict[code] = value
        }
        attributes = dict
    }
}

用途:

let attributes = try values.decodeIfPresent(AttributeDictionary.self, forKey: .attributes)
self.thumbnail = attributes?["thumbnail"]
self.metaTitle = attributes?["meta_title"]
self.metaDescription = attributes?["meta_description"]
© www.soinside.com 2019 - 2024. All rights reserved.