Swift 中 [String:Any] 字典的 JSON 解码错误

Error decoding JSON Dictionary of [String:Any] in Swift

我正在尝试解码一个名为 properties 的字典,它有 2 个键值对,具有不同类型的数据,String 和 Boolean。

{
    "type": "FeatureCollection",
    "query": [
        "loreto"
    ],
    "features": [
        {
            "id": "poi.738734375380",
            "type": "Feature",
            "place_type": [
                "poi"
            ],
            "relevance": 1,
            "properties": {
                "wikidata": "Q1932928",
                "landmark": true,
                "category": "airport",
                "maki": "airport"
            },
            "text": "Aeropuerto Internacional de Loreto",
            "place_name": "Aeropuerto Internacional de Loreto, Loreto, Baja California Sur 23889, Mexico",
            "matching_text": "Loreto International Airport",
            "matching_place_name": "Loreto International Airport, Loreto, Baja California Sur 23889, Mexico",
            "center": [
                -111.350714,
                25.990895
            ],
            "geometry": {
                "coordinates": [
                    -111.350714,
                    25.990895
                ],
                "type": "Point"
            },
            "context": [
                {
                    "id": "postcode.18084443266252890",
                    "text": "23889"
                },
                {
                    "id": "place.14237343392099110",
                    "wikidata": null,
                    "text": "Loreto"
                },
                {
                    "id": "region.4595447518930340",
                    "short_code": "MX-BCS",
                    "wikidata": "Q46508",
                    "text": "Baja California Sur"
                },
                {
                    "id": "country.1891876083773450",
                    "short_code": "mx",
                    "wikidata": "Q96",
                    "text": "Mexico"
                }
            ]
        },
        {
            "id": "region.7294174250099110",
            "type": "Feature",
            "place_type": [
                "region"
            ],
            "relevance": 1,
            "properties": {
                "short_code": "PE-LOR",
                "wikidata": "Q200938"
            },
            "text": "Loreto",
            "place_name": "Loreto, Peru",
            "bbox": [
                -77.810369,
                -8.645157,
                -69.962572,
                -0.029093
            ],
            "center": [
                -74.32,
                -4
            ],
            "geometry": {
                "type": "Point",
                "coordinates": [
                    -74.32,
                    -4
                ]
            },
            "context": [
                {
                    "id": "country.8104362620964510",
                    "short_code": "pe",
                    "wikidata": "Q419",
                    "text": "Peru"
                }
            ]
        },
        {
            "id": "place.13763862540099110",
            "type": "Feature",
            "place_type": [
                "place"
            ],
            "relevance": 1,
            "properties": {
                "wikidata": "Q124110"
            },
            "text": "Loreto",
            "place_name": "Loreto, Ancona, Italy",
            "bbox": [
                13.579312,
                43.416918,
                13.658326,
                43.45622
            ],
            "center": [
                13.60743,
                43.4403
            ],
            "geometry": {
                "type": "Point",
                "coordinates": [
                    13.60743,
                    43.4403
                ]
            },
            "context": [
                {
                    "id": "region.9523893847640810",
                    "short_code": "IT-AN",
                    "wikidata": "Q16114",
                    "text": "Ancona"
                },
                {
                    "id": "country.4747984886519910",
                    "short_code": "it",
                    "wikidata": "Q38",
                    "text": "Italy"
                }
            ]
        },
        {
            "id": "poi.2568390505832",
            "type": "Feature",
            "place_type": [
                "poi"
            ],
            "relevance": 1,
            "properties": {
                "landmark": true,
                "address": "Calle 5, La Urbina",
                "category": "italian restaurant, italian food, restaurant"
            },
            "text": "Loreto's",
            "place_name": "Loreto's, Calle 5, La Urbina, Sucre, Miranda, Venezuela",
            "center": [
                -66.808422,
                10.49214
            ],
            "geometry": {
                "coordinates": [
                    -66.808422,
                    10.49214
                ],
                "type": "Point"
            },
            "context": [
                {
                    "id": "place.13896838717891910",
                    "wikidata": "Q400079",
                    "text": "Sucre"
                },
                {
                    "id": "region.2525680865649430",
                    "short_code": "VE-M",
                    "wikidata": "Q191174",
                    "text": "Miranda"
                },
                {
                    "id": "country.5958724522570350",
                    "short_code": "ve",
                    "wikidata": "Q717",
                    "text": "Venezuela"
                }
            ]
        },
        {
            "id": "poi.2439541497917",
            "type": "Feature",
            "place_type": [
                "poi"
            ],
            "relevance": 1,
            "properties": {
                "landmark": true,
                "address": "Corredera baja de San Pablo, 6",
                "category": "spanish restaurant, spanish food, restaurant"
            },
            "text": "Loreto Coffee-Bar",
            "place_name": "Loreto Coffee-Bar, Corredera baja de San Pablo, 6, Madrid, Madrid 28004, Spain",
            "center": [
                -3.704493,
                40.421894
            ],
            "geometry": {
                "coordinates": [
                    -3.704493,
                    40.421894
                ],
                "type": "Point"
            },
            "context": [
                {
                    "id": "locality.5946271622443140",
                    "wikidata": "Q10387767",
                    "text": "Universidad"
                },
                {
                    "id": "postcode.9832348953129320",
                    "text": "28004"
                },
                {
                    "id": "place.10692955307562040",
                    "wikidata": "Q2807",
                    "text": "Madrid"
                },
                {
                    "id": "region.13206054317562040",
                    "short_code": null,
                    "wikidata": "Q2807",
                    "text": "Madrid"
                },
                {
                    "id": "country.8849824479570100",
                    "short_code": "es",
                    "wikidata": "Q29",
                    "text": "Spain"
                }
            ]
        }
    ],
    "attribution": "NOTICE: © 2019 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service (https://www.mapbox.com/about/maps/). This response and the information it contains may not be retained. POI(s) provided by Foursquare."
}

当我使用 JSONDecoder 时出现这个错误: "Expected to decode String but found a number instead."

Swift 中的可解码不允许使用 [String:Any] 我已经尝试过像数组一样解码,但是不,是一个字典。

struct SearchResult:Decodable{
    let type:String
    let features:[Place]
}

struct Place:Decodable {
    let place_name:String
    let properties:[String:String]
    let center:[Double]
}

我该怎么办?

问题出在这一行:

let properties:[String:String]

这里你说 properties 是字典。这还不够好。当然,在 JSON 中它是一个字典,但是要使用 JSONDecoder 解码字典,您需要一个与字典匹配的进一步嵌套结构。我们称之为属性。所以你会说

let properties:Properties

然后您将定义一个可解码的 Properties 结构。

但是,您遇到了一个问题:properties 词典并不都具有相同的键集。每次都有一些存在,一些不存在。要解决这个问题,请在定义 Properties 结构时使用 Optionals:

struct SearchResult:Decodable{
    let type:String
    let features:[Place]
}
struct Place:Decodable {
    let place_name:String
    let properties:Properties
    let center:[Double]
}
struct Properties:Decodable {
    let landmark : Bool?
    let address : String?
    let category : String?
    let wikidata : String?
    let short_code : String?
    let maki : String?
}

这似乎成功解码了您实际显示的 JSON。我明白了:

SearchResult(type: "FeatureCollection", features: [Place(place_name: "Aeropuerto Internacional de Loreto, Loreto, Baja California Sur 23889, Mexico", properties: Properties(landmark: Optional(true), address: nil, category: Optional("airport"), wikidata: Optional("Q1932928"), short_code: nil, maki: Optional("airport")), center: [-111.350714, 25.990894999999998]), Place(place_name: "Loreto, Peru", properties: Properties(landmark: nil, address: nil, category: nil, wikidata: Optional("Q200938"), short_code: Optional("PE-LOR"), maki: nil), center: [-74.319999999999993, -4.0]), Place(place_name: "Loreto, Ancona, Italy", properties: Properties(landmark: nil, address: nil, category: nil, wikidata: Optional("Q124110"), short_code: nil, maki: nil), center: [13.607430000000001, 43.440300000000001]), Place(place_name: "Loreto\'s, Calle 5, La Urbina, Sucre, Miranda, Venezuela", properties: Properties(landmark: Optional(true), address: Optional("Calle 5, La Urbina"), category: Optional("italian restaurant, italian food, restaurant"), wikidata: nil, short_code: nil, maki: nil), center: [-66.808421999999993, 10.492139999999999]), Place(place_name: "Loreto Coffee-Bar, Corredera baja de San Pablo, 6, Madrid, Madrid 28004, Spain", properties: Properties(landmark: Optional(true), address: Optional("Corredera baja de San Pablo, 6"), category: Optional("spanish restaurant, spanish food, restaurant"), wikidata: nil, short_code: nil, maki: nil), center: [-3.7044929999999998, 40.421894000000002])])

如果我遗漏了任何可能的键,请以相同的方式添加它们。检查 mapbox api 看看是否还有其他可能性。