Swift 4: JSONDecoder дает сбой в одном конкретном случае — операция не может быть завершена

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

Он работает с моей «сложной» структурой данных (которая реализует Codable) или даже с простым массивом Int, как показано ниже:

import Foundation

let jsonTextContainigArrayOfInt: String = "[1,2,3]"
let data = jsonTextContainigArrayOfInt.data(using: .utf8)!

do {
    let arrayOfInt: [Int] = try JSONDecoder().decode([Int].self, from: data)
    for n in arrayOfInt {
        print(n)
    }
}
catch {
    print(error)
}

Предыдущий код работает и правильно создает массив Int и печатает их.

Проблема возникает при использовании того же подхода с одним Int в JSON-тексте:

import Foundation

let jsonTextContainigOneInt: String = "1"
let data = jsonTextContainigOneInt.data(using: .utf8)!

do {
    let myInt: Int = try JSONDecoder().decode(Int.self, from: data)
    print(myInt)
}
catch {
    print(error)
}

Для этого второго подхода я получаю следующую ошибку:

"Операция не может быть выполнена"

*** Изменить ***

Отчет об ошибке для этого уже существует: https://bugs.swift.org/browse/SR-6163


person User Not Found    schedule 24.04.2018    source источник
comment
1 недействителен JSON.   -  person picciano    schedule 24.04.2018
comment
Спасибо за ваше время picciano. Не могли бы вы прокомментировать это: stackoverflow.com/a/7487892/8284660. В нем говорится, что такие значения, как одна строка, теперь должны быть действительными (в RFC 7159).   -  person User Not Found    schedule 24.04.2018
comment
Поскольку (NS)JSONSerialization не принимает его, это совсем не удивило бы и JSONDecoder. См. документацию: developer.apple.com/documentation/foundation/ Кроме того, похоже, что он совместим только с RFC 4627, а не с новым (github.com/gnustep/libs-base/blob/master/Source/)   -  person Larme    schedule 24.04.2018
comment
Используйте JSONSerialization, проверьте этот ответ stackoverflow.com/ вопросы/47941826/   -  person schmidt9    schedule 24.04.2018
comment
Спасибо вам обоим! Я видел, что JSONSerialization не принимает объекты, не являющиеся массивами и словарями, как допустимые JSON, хотя (как я сказал @picciano) в RFC-7159 теперь это допустимый JSON. Теперь я вижу, что JSONDecoder не имеет параметра .allowFragments, как в JSONSerialization. Спасибо за разъяснение @schmidt9.   -  person User Not Found    schedule 24.04.2018
comment
Спасибо всем за время!   -  person User Not Found    schedule 24.04.2018


Ответы (2)


JSONDecoder может декодировать только тип коллекции (массив или словарь) как корневой объект.

Под капотом JSONDecoder используется JSONSerialization без каких-либо опций. Однако для декодирования String или Int необходимо указать параметр .allowFragments.

Используйте JSONSerialization с опцией .allowFragments

let jsonTextContainingOneString = "1"
let data = Data(jsonTextContainingOneString.utf8)

do {
    let myString = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
    print(myString)
}
catch {
    print(error)
}
person vadian    schedule 24.04.2018

Интересно... Я нашел это:

https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/JSONSerialization.swift#L120

В частности, это защитное заявление:

open class JSONSerialization : NSObject {
        //...

        // top level object must be an Swift.Array or Swift.Dictionary
        guard obj is [Any?] || obj is [String: Any?] else {
            return false
        }

        //...
} 

Затем я посмотрел, следует ли считать простую текстовую строку действительным JSON, и, по-видимому, теперь следует (ранее он не принимался как действительный JSON). По крайней мере, на основе этого отличного ответа: https://stackoverflow.com/a/7487892/8284660

Это заставляет меня задаться вопросом, должно ли поведение моего исходного сообщения быть ошибкой или нет.

person User Not Found    schedule 24.04.2018
comment
Отчет об ошибке уже существует: bugs.swift.org/browse/SR-6163 - person User Not Found; 25.04.2018