typeMismatch on decodable Type when the type is correct

typeMismatch on decodable Type when the type is correct



When I hit my configuration API with Postman I am given the following json response back. In this response the two apiVersion keys are numbers and not strings.



"data":
"availability":
"auth": true,
"ab": true,
"cd": true
,
"helloWorldConfiguration":
"apiKey": "abcefg",
"rootUrl": "https://foo",
"apiVersion": 3
,
"fooBarConfiguration":
"baseUrl": "https://foo",
"apiVersion": 1,
"privateApiPath": "",
"publicApiPath": "dev",
"tokenPath": ""

,
"errors":



When I try to decode it it fails with a typeMismatch error. When I output the contents of the response, I see the following which looks fine to me.


typeMismatch


data =
availability =
auth = 1;
ab = 1;
cd = 1;
;
helloWorldConfiguration =
apiVersion = 1;
baseUrl = "https://foo";
privateApiPath = "";
publicApiPath = dev;
tokenPath = "";
;
fooBarConfiguration =
apiKey = abcefg;
apiVersion = 3;
rootUrl = "https://foo";
;
;
errors = (
);



The error given to me indicates that data.helloWorldConfiguration.apiVersion is of type string instead of int. We can see from the original HTTP response I get from Postman that's not the case.


data.helloWorldConfiguration.apiVersion



typeMismatch(Swift.Int, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), CodingKeys(stringValue: "helloWorldConfiguration", intValue: nil), CodingKeys(stringValue: "apiVersion", intValue: nil)], debugDescription: "Expected to decode Int but found a string/data instead.", underlyingError: nil))
21:17:40 ERROR Unable to decode the response data into a model representation.



My model represents those properties as integers so it would appear that it receives the response and considers those numbers to be strings, which they're not.


public struct ServerConfiguration: Decodable
let availability: AvailabilityConfiguration
let helloWorldConfiguration: HelloWorldConfiguration
let fooBarConfiguration: FooBarConfiguration

init(availability: AvailabilityConfiguration, helloWorldConfiguration: HelloWorldConfiguration, fooBarConfiguration: FloatSinkConfiguration)
self.availability = availability
self.helloWorldConfiguration = helloWorldConfiguration
self.fooBarConfiguration = fooBarConfiguration



public struct FooBarConfiguration: Decodable
let baseUrl: String
let apiVersion: Int
let privateApiPath: String
let publicApiPath: String
let tokenPath: String

init(baseUrl: String, apiVersion: Int, privateApiPath: String, publicApiPath: String, tokenPath: String)
self.baseUrl = baseUrl
self.apiVersion = apiVersion
self.privateApiPath = privateApiPath
self.publicApiPath = publicApiPath
self.tokenPath = tokenPath



public struct AvailabilityConfiguration: Decodable
let auth: Bool
let ab: Bool
let cd: Bool

init(auth: Bool, ab: Bool, cd: Bool)
self.auth = auth
self.ab = ab
self.cd = cd



public struct HelloWorldConfiguration: Codable
let apiKey: String
let rootUrl: String
let apiVersion: Int

init(apiKey: String, rootUrl: String, apiVersion: Int)
self.apiKey = apiKey
self.rootUrl = rootUrl
self.apiVersion = apiVersion




As you can see my apiVersion members are both of type integer along with the json response. What am I doing wrong here? I assume what's happening is Swift is considering the numbers in the json string, regardless of how they're actually represented in the json. Is that the case?


apiVersion


21:44:06 INFO GET: https:foo/configuration

"data" :
"availability" :
"auth" : true,
"ab" : true,
"cb" : true
,
"helloWorldConfiguration" :
"apiKey" : "abcd",
"rootUrl" : "https://foo",
"apiVersion" : "3"
,
"fooBarConfiguration" :
"baseUrl" : "https://foo",
"apiVersion" : "1",
"privateApiPath" : "",
"publicApiPath" : "dev",
"tokenPath" : "auth/token"

,
"errors" :



It would seem that despite the API correctly returning apiVersion as a number, Swift is turning it into a string. Am I decoding it incorrectly?


apiVersion


func getRoute<TResponseData: Decodable>(route:String, completion: @escaping (TResponseData) -> Void) throws {
let headers = try! self.getHeaders(contentType: ContentType.json)
let completeUrl: String = self.getUrl(route: route, requestUrl: nil)
logger.info("GET: (completeUrl)")
Alamofire.request(
completeUrl,
method: .get,
parameters: nil,
encoding: JSONEncoding.default,
headers: headers)
.validate()
.responseJSON (response) -> Void in
self.logger.info("GET Response: (String(describing:response.response?.statusCode))")

switch response.result
case .success(_):
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(Date.toFooBarDate)
do
let result = try decoder.decode(TResponseData.self, from: response.data!)
completion(result)
catch DecodingError.dataCorrupted(let error)
self.logger.error(error.underlyingError!)
return
catch
print(response.result.value!)
print(error)
self.logger.error("Unable to decode the response data into a model representation.")
return







Please create a String from the output and print this. The collection type output is ambiguous, because you cannot distinguish String and Int. And you can delete all initializers in the structs. You get them for free.
– vadian
Aug 20 at 4:39



print


String


Int





String representation of the Alamofire data provided. It does indeed appear to be a string, despite the API returning it as a number. Why would that be the case and how can I fix that?
– Johnathon Sullinger
Aug 20 at 4:47





apiversion is not and Int. Its string. Change it to String in your struct
– Amit
Aug 20 at 5:08


apiversion


Int


string


String


struct





In the Alamofire response output apiVersion is clearly String. Everything in double quotes is String. So the decoder error message is correct (actually it's always correct 😉 ).
– vadian
Aug 20 at 6:04



apiVersion


String


String





No, simply changing a data type should not be the solution. What if I actually need that to be a number so I ca do math or store it in a local DB as an integer? Hacking together a conversion after the fact is sloppy and a bandaid to the underlying issue of Alamofire’s data property having numbers decoded as strings. As mentioned earlier their response.result.value prints it correct though. Perhaps I just convert it to json string before decoding it.
– Johnathon Sullinger
Aug 20 at 7:22


data


response.result.value




1 Answer
1



I checked in my playground and it seems that everything is working fine.To find the real issue i think you are required to provide the real url from where you are getting json and can be checked with alamofire


import Foundation
let json = """

"data":
"availability":
"auth": true,
"ab": true,
"cd": true
,
"helloWorldConfiguration":
"apiKey": "abcefg",
"rootUrl": "https://foo",
"apiVersion": 3
,
"fooBarConfiguration":
"baseUrl": "https://foo",
"apiVersion": 1,
"privateApiPath": "",
"publicApiPath": "dev",
"tokenPath": ""

,
"errors":

"""
let data = json.data(using: .utf8)


struct Response : Codable
let data : Data?
let errors : [String]?

struct Availability : Codable
let auth : Bool?
let ab : Bool?
let cd : Bool?


struct Data : Codable
let availability : Availability?
let helloWorldConfiguration : HelloWorldConfiguration?
let fooBarConfiguration : FooBarConfiguration?


struct FooBarConfiguration : Codable
let baseUrl : String?
let apiVersion : Int?
let privateApiPath : String?
let publicApiPath : String?
let tokenPath : String?

struct HelloWorldConfiguration : Codable
let apiKey : String?
let rootUrl : String?
let apiVersion : Int?



let decoder = JSONDecoder()

let response = try decoder.decode(Response.self, from: data!)
print(response)



And here is the response



Response(data: Optional(__lldb_expr_11.Data(availability: Optional(__lldb_expr_11.Availability(auth: Optional(true), ab: Optional(true), cd: Optional(true))), helloWorldConfiguration: Optional(__lldb_expr_11.HelloWorldConfiguration(apiKey: Optional("abcefg"), rootUrl: Optional("https://foo"), apiVersion: Optional(3))), fooBarConfiguration: Optional(__lldb_expr_11.FooBarConfiguration(baseUrl: Optional("https://foo"), apiVersion: Optional(1), privateApiPath: Optional(""), publicApiPath: Optional("dev"), tokenPath: Optional(""))))), errors: Optional())





Why did you change all good non-optional declarations to bad optional ones?
– vadian
Aug 20 at 6:06





Its just for nothing because I don't know what will be optional and what will be definitely there
– Vikky
Aug 20 at 6:10





This doesn’t solve my problem unfortunately. I know that my model can parse the Postman response, if Alamofire provides it correct. Alamofire though is not since it’s converting the numbers to string. I can stand-up a temporary public API for you to hit in order to provide an example. Your example bypasses Alamofire where the weird behavior is occurring.
– Johnathon Sullinger
Aug 20 at 7:19





@JohnathonSullinger Exactly that's the reason I asked you for the URL
– Vikky
Aug 20 at 7:23






Ok will check and let you know
– Vikky
Aug 20 at 7:31






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

ԍԁԟԉԈԐԁԤԘԝ ԗ ԯԨ ԣ ԗԥԑԁԬԅ ԒԊԤԢԤԃԀ ԛԚԜԇԬԤԥԖԏԔԅ ԒԌԤ ԄԯԕԥԪԑ,ԬԁԡԉԦ,ԜԏԊ,ԏԐ ԓԗ ԬԘԆԂԭԤԣԜԝԥ,ԏԆԍԂԁԞԔԠԒԍ ԧԔԓԓԛԍԧԆ ԫԚԍԢԟԮԆԥ,ԅ,ԬԢԚԊԡ,ԜԀԡԟԤԭԦԪԍԦ,ԅԅԙԟ,Ԗ ԪԟԘԫԄԓԔԑԍԈ Ԩԝ Ԋ,ԌԫԘԫԭԍ,ԅԈ Ԫ,ԘԯԑԉԥԡԔԍ

How to change the default border color of fbox? [duplicate]

Henj