convert json to swift object return nill? - json

I am trying to convert JSON file to swift object, but I could not figure it out why it returns nill. as you use from code, I have two objects one Stakeholder and other MHSGroup I created a struct to hold data colled
StakeholderMHSGroup
struct StakeholderMHSGroup : Codable {
var stakeholders:[Stakeholder]?
var mhsGroups:[MhsGroup]?
}
main swift
let jsonStr = "{\"stakeholders\":[{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14bdf1c145f\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"},{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14bdf1c145d\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"},{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14bdf1c545f\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"},{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14baf1c145f\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"}],\"mhsGroups\":[{\"id\":\"495919eb-dcbc-48c5-99f5-48f6790b79e3\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"},{\"id\":\"495919eb-dcbc-48c5-99f5-48f6290b79e3\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"},{\"id\":\"495919eb-dcbc-48c5-99f5-48f6790b79e4\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"},{\"id\":\"495919eb-dcbc-48c5-99f5-48f6790b79e2\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"}]}"
"{"stakeholders":[{"id":"d95bb600-f63b-4ec7-bd2f-d14bdf1c145f","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"},{"id":"d95bb600-f63b-4ec7-bd2f-d14bdf1c145d","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"},{"id":"d95bb600-f63b-4ec7-bd2f-d14bdf1c545f","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"},{"id":"d95bb600-f63b-4ec7-bd2f-d14baf1c145f","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"}],"mhsGroups":[{"id":"495919eb-dcbc-48c5-99f5-48f6790b79e3","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"},{"id":"495919eb-dcbc-48c5-99f5-48f6290b79e3","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"},{"id":"495919eb-dcbc-48c5-99f5-48f6790b79e4","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"},{"id":"495919eb-dcbc-48c5-99f5-48f6790b79e2","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"}]}"
var data:Data?
data = jsonStr.data(using: .utf8)!
let userGroup = try! JSONDecoder().decode(StakeholderMHSGroup.self, from: data!)
print(userGroup)
update debug error
hread 1: Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.Double,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"mhsGroups", intValue: nil), _JSONKey(stringValue: "Index 0",
intValue: 0), CodingKeys(stringValue: "createdDate", intValue: nil)],
debugDescription: "Expected to decode Double but found a string/data
instead.", underlyingError: nil))

Judging from the error message, you seem to have declared createdDate as a Double, but in the JSON, the value associated with createdDate is a string.
You should declare createDate as a Date (you could always use createdDate.timeIntervalSince1970 if you want a Double), and set dateDecodingStrategy of the decoder to iso8601, because your dates seem to be in that format:
let decoder = JSONDecoer()
decoder.dateDecodingStrategy = .iso8601
// you shouldn't really use "try!" here...
let userGroup = try! decoder.decode(StakeholderMHSGroup.self, from: data!)

Related

Strava API returns null as first value of powerstream - Swift

When trying to use the Strava API with wattages per second I keep running into the problem of the first value always being null instead of a number.
struct streamsElement: Decodable {
struct Stream: Decodable {
let data: [Double]?
let series_type: String
let original_size: Int
let resolution: String
}
//decoding
if let wattsData = streams.watts?.data {
for watts in wattsData {
wattsArr.append("watts: \(watts)" )
}
}
"watts":{"data":[
null,
144,
98,
95, etc.
This works fine for all the other streams.
Error
Error decoding JSON data: valueNotFound(Swift.Double,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"watts", intValue: nil), CodingKeys(stringValue: "data", intValue:
nil), _JSONKey(stringValue: "Index 0", intValue: 0)],
debugDescription: "Expected Double but found null value instead.",
underlyingError: nil))
How can i turn this Null value into a zero so I don't get a JSON decoding error?
I tried to skip the first value in the decoding but this did not work.
You don´t have to turn the null JSON value into a 0 just to prevent the crash. Change the the type of the array to an optional and you are good to go. E.g.:
struct Stream: Decodable {
let data: [Double?] // add a ? if the array itself is optional too
let series_type: String
let original_size: Int
let resolution: String
}
I don´t know why and where you do what you described as decoding. But if you want your data Double´s to be nonoptional you could do something like this in a custom decoder:
struct streamsElement: Decodable {
struct Stream: Decodable {
let data: [Double]
enum CodingKeys: CodingKey{
case data
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// decode the data property to the array of optionals
let data = try container.decode([Double?].self, forKey: .data)
// replace nil values with 0
self.data = data.map{$0 ?? 0}
}
}
var watts: Stream
}
I´ve implemented only the problematic property. Of course you would need to expand this to the others.

Swift Error wift Json Decoder Fatal error

when try a json file to decode, with the Jsondecoder().decode i get the errwor:
Thread 7: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
what does the compiler want to tell me and where is the error in Code?
URLSession.shared.dataTask(with: url) { (dataLocation, _, _) in
if let dataLocation = dataLocation {
let JsonData = try! JSONDecoder().decode([LandmarkLocation].self, from: dataLocation)
print(JsonData.count)
}
}
.resume()
I also added the decodable protocol to the LandmarkLocation structure
Try not to decode the LandmarkLocation object as an array
if let dataLocation = dataLocation {
let JsonData = try JSONDecoder().decode(LandmarkLocation.self, from: dataLocation)
print(JsonData.count)
}

Having trouble decoding JSON file in swift

I am trying to decode a JSON format from CalorieNinja API but it appears that the equals signs in their jsons are throwing my code off. Here is my code for decoding the JSON file:
let dataTask = session.dataTask(with: request) { (data, response, error) in
//check errors
if error == nil && data != nil {
let decoder = JSONDecoder()
do{
let result = try decoder.decode(Result.self, from: data!)
print(result)
}catch{
print("there was an error")
print(error)
}
}
}
Here are my structs:
struct FoodItem: Codable {
var name: String?
var calories: String?
}
struct Result: Codable {
var items: [FoodItem]?
}
Here is the JSON format that gets returned from CalorieNinjas(this is just an example this is not the output of my code):
{
items = (
{
calories = "18.2";
"carbohydrates_total_g" = "3.9";
"cholesterol_mg" = 0;
"fat_saturated_g" = 0;
"fat_total_g" = "0.2";
"fiber_g" = "1.2";
name = tomato;
"potassium_mg" = 23;
"protein_g" = "0.9";
"serving_size_g" = 100;
"sodium_mg" = 4;
"sugar_g" = "2.6";
}
);
}
And lastly here is the error if it helps at all:
typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "items", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "calories", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
You can check CalorieNinja API again. I think the calories field should be Double? due to their document.

How to decode JSON object with any number of key–value pairs in Swift?

I’m using Swift to try and decode JSON:API-formatted JSON results. The JSON I’m trying to parse has a shape like this:
{
"data": {
"type": "video",
"id": "1",
"attributes": {
"name": "Test Video",
"duration": 1234
}
}
}
I’m trying to create a Swift struct that will encode these JSON objects, but I’m having issues with the attributes key as it could contain any number of attributes.
The Swift structs I’m trying to encode the above into look like this:
struct JSONAPIMultipleResourceResponse: Decodable {
var data: [JSONAPIResource]
}
struct JSONAPIResource: Decodable {
var type: String
var id: String
var attributes: [String, String]?
}
The type and id attributes should be present in every JSON:API result. The attributes key should be a list of any number of key–value pairs; both the key and value should be strings.
I’m then trying to decode a JSON response from an API like this:
let response = try! JSONDecoder().decode(JSONAPIMultipleResourceResponse.self, from: data!)
The above works if I leave the type and id properties in my JSONAPIResource Swift struct, but as soon as I try and do anything with attributes I get the following error:
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "attributes", intValue: nil), _JSONKey(stringValue: "poster_path", intValue: nil)], debugDescription: "Expected String but found null value instead.", underlyingError: nil)): file /Users/[User]/Developer/[App]/LatestVideosQuery.swift, line 35
2020-07-14 16:13:08.083721+0100 [App][57157:6135473] Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "attributes", intValue: nil), _JSONKey(stringValue: "poster_path", intValue: nil)], debugDescription: "Expected String but found null value instead.", underlyingError: nil)): file /Users/[User]/Developer/[App]/LatestVideosQuery.swift, line 35
I get Swift is very strongly typed, but I’m unsure on how to encode this unstructured data in Swift. My plan is to have generic JSONAPIResource representations of resources coming back from my API, then I can then map into model structs in my Swift application, i.e. convert the above JSON:API resources into a Video struct.
Also, I’m trying to naïvely convert values in the attributes object to strings but as you may see, duration is an integer value. If there’s a way to have attributes in my JSONAPIResource struct retain primitive values such as integers and booleans and not just strings, I’d be keen to read how!
If it's a completely generic bag of key/values (which might indicate a need for a possible design change), you can create an enum to hold the different (primitive) values that JSON can hold:
enum JSONValue: Decodable {
case number(Double)
case integer(Int)
case string(String)
case bool(Bool)
case null
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let int = try? container.decode(Int.self) {
self = .integer(int)
} else if let double = try? container.decode(Double.self) {
self = .number(double)
} else if let string = try? container.decode(String.self) {
self = .string(string)
} else if let bool = try? container.decode(Bool.self) {
self = .bool(bool)
} else if container.decodeNil() {
self = .null
} else {
// throw some DecodingError
}
}
}
and then you could set attributes to:
var attributes: [String: JSONValue]
If there is a consistent relationship between the type value and the key-value pairs in attributes I recommend to declare attributes as enum with associated types and decode the type depending on the type value.
For example
let jsonString = """
{
"data": {
"type": "video",
"id": "1",
"attributes": {
"name": "Test Video",
"duration": 1234
}
}
}
"""
enum ResourceType : String, Decodable {
case video
}
enum AttributeType {
case video(Video)
}
struct Video : Decodable {
let name : String
let duration : Int
}
struct Root : Decodable {
let data : Resource
}
struct Resource : Decodable {
let type : ResourceType
let id : String
let attributes : AttributeType
private enum CodingKeys : String, CodingKey { case type, id, attributes }
init(from decoder : Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.type = try container.decode(ResourceType.self, forKey: .type)
self.id = try container.decode(String.self, forKey: .id)
switch self.type {
case .video:
let videoAttributes = try container.decode(Video.self, forKey: .attributes)
attributes = .video(videoAttributes)
}
}
}
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Root.self, from: data)
print(result)
} catch {
print(error)
}

How can I decode date string to Date object from JSON?

How can I decode timestamp to Date from JSON?
I get my date from the server as Json like this:
{
"date": "2610-02-16T03:16:15.143Z"
}
and im trying to build a Date class from it:
class Message : Decodable {
var date: Date
}
its not working as Expected I am getting this error:
Failed to fetch messages: typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "date", intValue: nil)], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil))
When decoding date info like this, you need to use a custom dateDecodingStrategy, and set the date parser's timezone and locale:
let data = """
{
"date": "2610-02-16T03:16:15.143Z"
}
""".data(using: .utf8)!
struct Message: Codable {
let date: Date
}
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "GMT")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let message = try decoder.decode(Message.self, from: data)
print(message.date)
} catch {
print(erroor)
}