Кодирование выдает ошибку: «Данные не могут быть записаны, поскольку они не в правильном формате»

У меня есть один API, где данные параметров меняются в зависимости от параметров.

Ответ 1


{
  "success": true,
  "statusCode": 200,
  "errorLst": [],
  "succcessMessage": null,
  "resultData": [
    {
      "plotSize": 1,
      "equationValue": null
    },
    {
      "plotSize": 2,
      "equationValue": "*1.5"
    },
    {
      "plotSize": 3,
      "equationValue": "*2.5"
    }
  ]
}

Ответ 2

{
  "success": true,
  "statusCode": 200,
  "errorLst": [],
  "succcessMessage": null,
  "resultData": [
    {
      "plotSize": 4,
      "equationValue": "*4"
    },
    {
      "plotSize": 5,
      "equationValue": "*5.5"
    },
    {
      "plotSize": 6,
      "equationValue": "*6.5"
    }
  ]
}

Я анализирую данные API и пытаюсь преобразовать данные базовой модели в данные моего фактического режима:

let myEndPoint = EndPoint(modelType: PlotModel.self)
self.plotModelModel = convertToModel(baseModelResponse: inner_apiResponse, endpoint: myEndPoint) as? PlotModel ?? PlotModel()

inner_apiResponse — это не что иное, как данные, которые я получаю от API, анализируемые как BaseModel

У меня есть модель, как показано ниже:

struct PlotModel : Codable {
    var success : Bool?
    var statusCode : Int?
    var succcessMessage : String?
    
    var resultData : [InnerPlotData]?
    
}

struct InnerPlotData : Codable {
    var plotSize : Int?
    var equationValue : String?
}

Я использую функцию ниже для преобразования базовой модели в мою модель:

func convertToModel<T : Codable>(baseModelResponse : BaseModel, endpoint : EndPoint<T>) -> Any {
    
    do {
        let encoder = JSONEncoder()
        do {
            let jsonData = try encoder.encode(baseModelResponse)
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                if let data = jsonString.data(using: .utf8) {
                    do {
                        let localModel = try JSONDecoder().decode(endpoint.modelType.self, from: data)
                        return localModel
                    } catch {
                        print("Error parsing JSON:", error)
                        return makeErrorBaseModel()
                    }
                } else {
                    print("Invalid JSON string")
                    return makeErrorBaseModel()
                }
            } else {
                return makeErrorBaseModel()
            }
        } catch let decodingError as DecodingError {
            switch decodingError {
                case .typeMismatch(_, let c), .valueNotFound(_, let c), .keyNotFound(_, let c), .dataCorrupted(let c):
                    "error.debugDescription=c===\(c.debugDescription)".printLog()
                    return makeErrorBaseModel()
            }
        } catch {
            "error.debugDescription==\(error.localizedDescription)".printLog()
            return makeErrorBaseModel()
        }
    } catch {
        return makeErrorBaseModel()
    }
    
}

У меня есть модель, как показано ниже:

struct BaseModel : Codable {
    var success : Bool?
    var statusCode : Int?
    var succcessMessage : String?
    
    var resultData : AnyCodable?
    
}

@objcMembers final class AnyCodable: NSObject, Codable {
    
    let value: Any?
    
    init(_ value: Any?) {
        self.value = value
    }
    
    required init(from decoder: Decoder) throws {
        
        let container = try decoder.singleValueContainer()
        
        if let value = try? container.decode(String.self) {
            self.value = value
        } else if let value = try? container.decode(Bool.self) {
            self.value = value
        } else if let value = try? container.decode(Int.self) {
            self.value = value
        } else if let value = try? container.decode(Double.self) {
            self.value = value
        } else if let value = try? container.decode(Float.self) {
            self.value = value
        } else if container.decodeNil() {
            self.value = nil
        } else if let value = try? container.decode([String: AnyCodable].self) {
            self.value = value.mapValues { $0.value }
        } else if let value = try? container.decode([AnyCodable].self) {
            self.value = value.map { $0.value }
        }  else {
            throw DecodingError.dataCorruptedError(
                in: container,
                debugDescription: "Invalid value cannot be decoded"
            )
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch value {
            case let bool as Bool:
                try container.encode(bool)
            case let int as Int:
                try container.encode(int)
            case let float as Float:
                try container.encode(float)
            case let double as Double:
                try container.encode(double)
            case let string as String:
                try container.encode(string)
            case let array as [Any?]:
                try container.encode(array.map { AnyCodable($0) })
            case let dictionary as [String: Any?]:
                try container.encode(dictionary.mapValues { AnyCodable($0) })
            case let encodable as Encodable:
                try encodable.encode(to: encoder)
            default:
                let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded")
                throw EncodingError.invalidValue(value ?? "InvalidValue", context)
        }
    }
}

Ответ 2 работает нормально, однако ответ 1 выдает ошибку, как показано ниже:

error.debugDescription==The data couldn’t be written because it isn’t in the correct format.

Исключение выдается в строке let jsonData = try encoder.encode(baseModelResponse).

Почему это происходит и как это можно исправить?

Минимальный код для проверки

https://wetransfer.com/downloads/08ebea0e519f58023444c36727e4afdc20240820150159/adf188


После дальнейшей проверки согласно вадиану я обнаружил, что в func encode я получаю исключение, как показано ниже.

validValue("InvalidValue", Swift.EncodingError.Context(codingPath: [CodingKeys(stringValue: "resultData", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), _JSONKey(stringValue: "equationValue", intValue: nil)], debugDescription: «Значение AnyEncodable не может быть закодировано», базовая ошибка: nil))

Однако equationValue имеет тип String, как указано в модели.

Если вы заметили, уравнение ValueValue имеет нулевое значение в первой строке, и AnyCodable устанавливает его тип как Any, а в следующем месте я получаю данные как String, поэтому я получаю эту ошибку. Может быть...

Fahim Parkar 20.08.2024 12:48

Не могли бы вы предоставить MRE ?

Joakim Danielson 20.08.2024 13:00

@JoakimDanielson: Хорошо, я сделаю это сегодня, когда освобожусь...

Fahim Parkar 20.08.2024 13:03

Возможно, я что-то упускаю, но почему бы вам просто не декодировать тело ответа напрямую, пытаясь получить PlotModel? Или, может быть, используйте общий Response<T>, соответствующий Decodeable, где T — декодируемый тип того, что есть resultData. Затем вы можете «развернуть» фактические данные типа T со свойством resultData и положить этому конец ;) Значит, вам больше ничего не нужно — только это Result<T>. Просто убедитесь, что T также соответствует декодируемому.

CouchDeveloper 20.08.2024 13:07

В среде Codable всегда печатайте error, а не его localizedDescription. Вы отбрасываете настоящую описательную ошибку.

vadian 20.08.2024 13:51

@JoakimDanielson : wetransfer.com/downloads/… Пожалуйста, проверьте apiURLString и журналы на наличие ошибок...

Fahim Parkar 20.08.2024 17:05

@vadian: Я обновил вопрос со ссылкой на код... Можете ли вы проверить, если возможно...

Fahim Parkar 20.08.2024 17:06

@vadian: спасибо за сохранение... Я получаю ошибку "invalidValue("InvalidValue", Swift.EncodingError.Context(codingPath: [CodingKeys(stringValue: "resultData", intValue: nil), _JSONKey(stringValue: "Index 0) ", intValue: 0), _JSONKey(stringValue: "equationValue", intValue: nil)], debugDescription: "Любое кодируемое значение не может быть закодировано", базовая ошибка: ноль))"

Fahim Parkar 20.08.2024 17:10

@JoakimDanielson: В своем коде я не добавил var equationValue : String? внутри RentEstimatorInnerVisibleConfigPlotSizesModel модели. Вы можете добавить, чтобы проверить дальше. Я не добавлял, так как не хочу им пользоваться, он для меня бесполезен. Также проверьте сообщение выше, чтобы проверить причину исключения...

Fahim Parkar 20.08.2024 17:16

Ошибка означает то, что написано: «Значение AnyEncodable не может быть закодировано». Вы боретесь с фреймворком. Codable не поддерживает Any. Традиционный JSONSerialization не вариант?

vadian 20.08.2024 17:23
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
0
10
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Значение, которое терпит неудачу:

  "equationValue": null

Вы забыли указать случай для этого:

case nil:
    try container.encodeNil()

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

Спасибо за быстрый ответ. Я изучу это и отвечу вам...

Fahim Parkar 21.08.2024 08:47

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