Как декодировать пользовательские значения JSON с помощью JSONDecoder

Бэкэнд возвращает пользовательское значение JSON для местоположения. Как показано в примере:

{
    "location": (54.000000, 21.000000)
}

Для разбора JSON я использую этот код:

let json = """
{
    "location": (54.000000, 21.000000)
}
"""    
struct Location: Codable {
    var latitude: Double
    var longitude: Double
}
let dataJson = json.data(using: .utf8)!
let location = try? JSONDecoder().decode(Location.self, from: dataJson)

Когда я пытаюсь создать объект Location с помощью JSONDecoder, я получаю сообщение об ошибке: Указанные данные недействительны в формате JSON.

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 18." UserInfo={NSDebugDescription=Invalid value around character 18.})))

Я знаю, что это недействительный JSON. Какие методы переопределить, чтобы я мог анализировать недопустимые значения JSON?


person Ramis    schedule 30.01.2018    source источник
comment
Разве вы не можете изменить свой сервер, чтобы вернуть действительный JSON?   -  person    schedule 30.01.2018
comment
@Balanced Backend управляется третьей стороной, и вносить изменения невозможно.   -  person Ramis    schedule 31.01.2018


Ответы (1)


Если третья сторона генерирует недопустимый JSON согласованным образом, вы можете использовать регулярное выражение, чтобы вернуть его к действительному JSON. Это не надежно. Это может потерпеть неудачу, если JSON просто отформатирован по-другому. Лучше всего попросить третью сторону исправить их серверную часть.

Вы можете использовать регулярное выражение для замены круглых скобок квадратными скобками:

var json = """
{
"location": (54.000000, 21.000000)
}
"""

let regex = try! NSRegularExpression(pattern: "\\\"location\\\":\\s*\\((.+?)\\)", options: [])
let fullRange = NSRange(..<json.endIndex, in: json)

json = regex.stringByReplacingMatches(in: json, options: [], range: fullRange, withTemplate: "\"location\": [$1]")

Вам также необходимо добавить собственный декодер в вашу структуру Location, поскольку теперь она закодирована как массив:

struct Location: Decodable {
    var latitude: Double
    var longitude: Double

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        latitude = try container.decode(Double.self)
        longitude = try container.decode(Double.self)
    }
}

Пример расшифровки:

struct Response: Decodable {
    var location: Location
}
let dataJson = json.data(using: .utf8)!
let location = try JSONDecoder().decode(Response.self, from: dataJson)
person Mike Henderson    schedule 01.02.2018