Parsing JSON GET response with special characters

Hi all,

I seem to be having trouble parsing a JSON response from a server that has a unique naming convention for the first item in the object:
Code Block
{
"@odata.context": "$metadata#Member",
"value": [
{
"MemberKeyNumeric": 123456,
"MemberOfficeNumeric": 123456,
....
},
{
.....
}
]
}

I've written two decodable structs to deal with this, however the naming convention of the first item is throwing an error...
Code Block
struct wasatchResponse:Decodable{
    var @odata.context:String
    var value:[valueData]
}
struct valueData:Decodable {
    var MemberKeyNumeric:Int
    var MemberOfficeNumeric:Int


I am pretty new to swift, so I could be doing this incorrectly? I guess my biggest question is there a way to have swift ignore the '@' and the '.' items, similar to Java using a @SerializedName() argument?

Thanks.

Accepted Reply

You should use CodingKeys:

Code Block
struct ValueData: Decodable {
var memberKeyNumeric:Int
var memberOfficeNumeric:Int
enum CodingKeys : String, CodingKey {
case memberKeyNumeric = "MemberKeyNumeric"
case memberOfficeNumeric = "MemberOfficeNumeric"
}
}
struct wasatchResponse: Decodable {
var context: String
var value:[ValueData]
enum CodingKeys : String, CodingKey {
case context = "@odata.context"
case value = "value"
}
}

And take care to follow Swift naming conventions:
var starts with lowercase ; struct names with Uppercase.

Tell if that works and what you get when decoding.

Replies

You should use CodingKeys:

Code Block
struct ValueData: Decodable {
var memberKeyNumeric:Int
var memberOfficeNumeric:Int
enum CodingKeys : String, CodingKey {
case memberKeyNumeric = "MemberKeyNumeric"
case memberOfficeNumeric = "MemberOfficeNumeric"
}
}
struct wasatchResponse: Decodable {
var context: String
var value:[ValueData]
enum CodingKeys : String, CodingKey {
case context = "@odata.context"
case value = "value"
}
}

And take care to follow Swift naming conventions:
var starts with lowercase ; struct names with Uppercase.

Tell if that works and what you get when decoding.
Hi @Claude31,

Thank you for the response! After redoing the naming conventions and creating the coding keys, the first problem was my URL request, which then once I fixed (and validated by printing the response) I then couldn't seem to get the response to parse into the individual "ValueData" elements...

Code Block    
func getMember (completion: @escaping(Result<[ValueData], WasatchError>) -> Void){
        let task = URLSession.shared.dataTask(with: resourceRequest) {(data, response, error) in
            guard let jsonData = data else {
                completion(.failure(.noData))
                return }
            do {
                let wasatchResponse = try JSONDecoder().decode(WasatchResponse.self, from: jsonData)
                let values = wasatchResponse.value
                completion(.success(values))
            }catch{
                completion(.failure(.cannotProcessData))
            }
            print(String(data: jsonData, encoding: .utf8)!)
        }
        task.resume()
    }

What's interesting is that last print statement still shows me the JSON structure I'm looking for, however for some reason I'm still throwing the "completion(.failure(.cannotProcessData))" statement.

Could having non-matching data types make the JSON parsing fail? (i.e. my struct defining a string when the JSON response is a bool?)

Could having non-matching data types make the JSON parsing fail? (i.e. my struct defining a string when the JSON response is a bool?)

Yes, decode will fail.

Could you show the complete JSON you have with print of line 16 ?

may read this to see various ways to handle missing data (notably making properties optional)
https://stackoverflow.com/questions/46344963/swift-jsondecode-decoding-arrays-fails-if-single-element-decoding-fails
Sure! The following is what we are looking for...

Code Block
{
"@odata.context": "$metadata#Member",
"value": [
{
"MemberKeyNumeric": 87205,
"OfficeKeyNumeric": 70083,
"MemberAORkey": "M00000628",
"MemberAddress1": "230 West Towne Ridge Pkwy",
"MemberAddress2": "",
"MemberCity": "Sandy",
"MemberFax": "",
"MemberFirstName": "XBroker",
"MemberFullName": "XBroker Test",
"MemberKey": "87205",
"MemberLastName": "Test",
"MemberMiddleName": "",
"MemberMlsId": "87205",
"MemberMobilePhone": "801-676-5400",
"MemberNationalAssociationId": "12345",
"MemberOfficePhone": "801-676-5400",
"MemberPostalCode": "84070",
"MemberPreferredPhone": "801-676-5400",
"MemberStateLicense": "0",
"OfficeKey": "70083",
"OfficeMlsId": "70083",
"OfficeName": "Fake Brokerage",
"OriginatingSystemMemberKey": "M00000628",
"OriginatingSystemName": "UtahRealEstate.com",
"MemberMlsAccessYN": true,
"ModificationTimestamp": "2020-12-18T16:23:49Z",
"OriginalEntryTimestamp": null,
"MemberAOR": "Salt Lake Board",
"MemberCountry": "",
"MemberCountyOrParish": "",
"MemberStateLicenseState": "UT",
"MemberStateOrProvince": "UT",
"MemberStatus": "Active",
"MemberType": "MLS Only Broker",
"MemberDesignation": ""
}
]
}

And that makes more sense, I was wondering if the types would affect the outcome. I can go through the data dictionary to ensure I am getting the correct types for each of these fields.