在Swift中解码[String:Any]的JSON字典时出错

时间:2019-06-02 23:47:27

标签: json swift dictionary decodable

我正在尝试解码一个名为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时发生此错误: “希望对String进行解码,但是找到了一个数字。”

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

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

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

我该怎么办?

1 个答案:

答案 0 :(得分:1)

问题是这一行:

let properties:[String:String]

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

let properties:Properties

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

但是,您遇到了一个问题:properties词典并不都具有相同的键集。每次都有一些存在,有些则没有。要解决此问题,请在定义“属性”结构时使用“可选”:

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(类型:“ FeatureCollection”,功能:[Place(place_name:“墨西哥洛洛托国际机场,洛雷托,墨西哥下加利福尼亚州苏里23889”,属性:属性(具有里程碑意义:可选(true),地址:无,类别:可选(“机场”),维基数据:可选(“ Q1932928”),短代码:无,马基:可选(“机场”)),中心:[-111.350714,25.990894999999998]),地点(地点名称:“洛雷托,秘鲁” ,属性:属性(地标:无,地址:无,类别:无,wikidata:可选(“ Q200938”),短代码:可选(“ PE-LOR”),maki:无),中心:[-74.319999999999993,-4.0 ]),地点(地点名称:“洛雷托,安科纳,意大利”,属性:属性(地标:无,地址:无,类别:无,Wikidata:可选(“ Q124110”),短代码:无,马基:无),中心:[13.607430000000001,43.440300000000001]),地点(地名:“洛雷托\,Calle 5,La Urbina,苏克雷,米兰达,委内瑞拉”),属性:Properties(地标:Optional(true),地址:Optional(“ Calle 5, La Urbina”),类别:可选(“ italian r餐馆,意大利美食,餐厅”),维基百科数据:无,简码:无,马基:无),中心:[-66.808421999999993、10.492139999999999]),地点(地名:“洛雷托咖啡吧,圣巴勃罗·德·巴雷德·德·巴勃罗,6岁,马德里,马德里28004,西班牙”,属性:属性(地标:Optional(true),地址:Optional(“ Corredera baja de San Pablo,6”),类别:Optional(“西班牙餐厅,西班牙美食,餐厅”),wikidata :nil,简码:nil,maki:nil),中心:[-3.7044929999999998,40.421894000000002])])

如果我错过了任何可能的键,只需以相同的方式添加它们。请使用mapbox api检查是否还有其他可能性。