Несоответствие типов ошибок декодирования Swift с типом Bool

import Foundation

let json = """
{
    "property": null
}
""".data(using: .utf8)!

struct Program<T: Decodable>: Decodable {
    let property: T
    
    static func decode() {
        do {
            try JSONDecoder().decode(Self.self, from: json)
        } catch {
            print("Error decoding \(T.self): \(error)\n")
        }
    }
}

Program<String>.decode()
Program<Int>.decode()
Program<[Double]>.decode()
Program<[String: Int]>.decode()
Program<Bool>.decode()

Для каждого случая, кроме Bool, мы получаем ошибку valueNotFound("Невозможно получить контейнер декодирования без ключа - вместо этого обнаружено нулевое значение"). Это правильно, согласно документации.
Только для Bool по какой-то причине мы получаем ошибку typeMismatch("Ожидали декодирования Bool, но вместо этого нашли ноль.").

Можете ли вы воспроизвести то же самое в Linux? Если нет, то я думаю, что для этого нет особой причины. Кто-то ошибся, и всё.

Sweeper 24.03.2024 05:44

@Sweeper Боюсь, у меня нет Linux, чтобы попробовать 🙃

Roman 24.03.2024 05:55
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
3
2
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В Linux (рабочий пример ) [Double] создает valueNotFound, а все остальное создает typeMismatch. Это согласуется с исходным кодом Swift-Core-Libs-Foundation , где unkeyedContainer (который вызывается при декодировании массива) выбрасывает valueNotFound ( источник):

@usableFromInline func unkeyedContainer() throws -> UnkeyedDecodingContainer {
    switch self.json {
    case .array(let array):
        ...
    case .null:
        throw DecodingError.valueNotFound([String: JSONValue].self, DecodingError.Context(
            codingPath: self.codingPath, 
            debugDescription: "Cannot get unkeyed decoding container -- found null value instead"
        ))
    default:
        ...
    }
}

тогда как typeMismatch выбрасывается контейнером с одним значением, созданным для декодирования «скалярных» типов (источник).

func decode(_: Bool.Type) throws -> Bool {
    guard case .bool(let bool) = self.value else {
        throw self.impl.createTypeMismatchError(type: Bool.self, value: self.value)
    }

    return bool
}

Из-за этого я подозреваю, что JSONDecoder в macOS не реализован специально для Bool, чтобы бросать typeMismatch и остальных valueNotFound. Кажется, сначала используется API JSONSerialization для декодирования JSON в NSDictionary. В результате поведение Bool по сравнению с другими типами может быть странным.

Лично я считаю, что формулировки typeMismatch и valueNotFound в документации достаточно расплывчаты, поэтому было бы «правильно» выбросить любой из них при обнаружении нулевого значения.

Интересное расследование. Тем не менее, мне это кажется ошибкой: valueNotFound должно быть правильным в тех случаях, когда указано значение JSON null и ожидается значение.

CouchDeveloper 24.03.2024 10:20

@CouchDeveloper Мне не удалось найти отчет об этой ошибке. Не стесняйтесь сообщить о проблеме в apple/swift-core-libs-foundation (или, возможно, в apple/swift?)

Sweeper 24.03.2024 10:30

При декодировании JSON с помощью JSONDecoder Swift, если необязательный тип, такой как String, Int, [Double] или [String: Int], встречает нулевое значение, он выдает ошибку valueNotFound, поскольку не может декодировать значение null в необязательный тип.

Однако для Bool, которое может быть только истинным или ложным, обнаружение значения null вызывает ошибку typeMismatch, поскольку значение null не считается допустимым логическим значением.

Чтобы справиться с этой проблемой, вы можете сделать тип свойства необязательным, если в JSON вы ожидаете нулевые значения.

struct Program<T: Decodable>: Decodable {
    let property: T?
    
    static func decode() {
        do {
            try JSONDecoder().decode(Self.self, from: json)
        } catch {
            print("Error decoding \(T.self): \(error)\n")
        }
    }
}

Другие вопросы по теме