Parsing nested JSON using Decodable's init(from decoder:) - json

I'm decoding JSON using Decodable with init(from decoder:) but unable to parse out my nested JSON.
The JSON I'm trying to parse:
The main conflict I believe is parsing the array of edges.
The goal is to get a model I can use with key, value, and namespace.
Would need to use init(from decoder:).
{
"data": {
"collectionByHandle": {
"metafields": {
"edges": [
{
"node": {
"key": "city",
"value": "Fayetteville ",
"namespace": "shipping"
}
},
{
"node": {
"key": "country",
"value": "United States",
"namespace": "shipping"
}
},
{
"node": {
"key": "state",
"value": "AR",
"namespace": "shipping"
}
}
]
}
}
},
"errors": null
}
This technically works but wanting to use init(from decoder: ) and return a model that just has key, value, and namespace without having multiple separate structs/classes.
struct Shipping: Codable {
let data: DataClass
let errors: String?
}
struct DataClass: Codable {
let collectionByHandle: CollectionByHandle
}
struct CollectionByHandle: Codable {
let metafields: Metafields
}
struct Metafields: Codable {
let edges: [Edge]
}
struct Edge: Codable {
let node: Node
}
struct Node: Codable {
let key, value, namespace: String
}

Related

deal with deep nested json in swift decodable

I have this deep nested JSON:
{
"response": {
"status": {
"status_code": 200,
"execute_time": 0.0068638324737549
},
"list": {
"beers": [
{
"beer": {
"id": 323,
"name": "Saint Arnold",
"style": "IPA"
}
},
{
"beer": {
"id": 873,
"name": "Black Angel",
"style": "Stout"
}
},
]
}
}
}
I would like to create just a struct like:
struct Beer {
let id: Int
let name: String
let style: String
init(from decoder: Decoder) throws {
... custom decode ...
}
}
and make a custom decode to decode from json just to this model and not to create a struct for every level of the json.
I don't know where to start, someone could help?
I tried to play with container.encode() but I can't understand how it's work in my case.

How to create a Swift model for JSON

{"dataList":{"1547795650562": {
"c0a8007b-6759-111d-8167-59e8dabe0086": {
"recordDate": 1547795650562,
"resultValue": "160",
"vitalParameter": {
"uom": {
"code": "KG",
"name": "KG",
"id": "c0a8007b-6759-111d-8167-59e76204007f"
},
"resultType": {
"code": "VSRTNUMERIC",
"name": "Numeric",
"id": "20cf4756-40b0-4cc1-acb5-861765370a41"
},
"code": "29463-7",
"name": "Weight",
"id": "c0a8007b-6759-111d-8167-59e8dabe0086"
},
"id": "c0a8007b-6855-1d16-8168-5fd18fa301b7"
}}
}}
getting 1547795650562 and c0a8007b-6759-111d-8167-59e8dabe0086 as class names. But I dont want like this;
class DataList : NSObject, NSCoding{
var 1547795650562 : 1547795650562!
}
class 1547795650562 : NSObject, NSCoding{
var c0a8007b6759111d816759e8dabe0086 : VitalParameter!
}
But the problem here is, 1547795650562 and c0a8007b-6759-111d-8167-59e8dabe0086 cannot be hard coded because they may change.
c0a8007b-6759-111d-8167-59e8dabe0086 is dynamic id and 1547795650562 is recordDate. Inner object is repetitive.
But I have to map as the keys are of recordDate and id respectively.
Try using Codable instead of NSCoding to parse your JSON data.
Models:
struct Root: Codable {
let dataList: [String:[String:Record]]
}
struct Record: Codable {
let recordDate: Int
let resultValue: String
let vitalParameter: VitalParameter
let id: String
}
struct VitalParameter: Codable {
let uom, resultType: ResultType
let code, name, id: String
}
struct ResultType: Codable {
let code, name, id: String
}
Parse the JSON data using above models like,
do {
let response = try JSONDecoder().decode(Root.self, from: data)
print(response)
} catch {
print(error)
}
Note: You can use https://app.quicktype.io to get the models from your JSON instantly. Make the changes as per your requirement and you're good to go.

How do you parse recursive JSON in Swift?

I am receiving JSON from a server where the data is recursive. What is the best way to parse this into a convenient Swift data structure?
Defining a Swift Codable data structure to parse it into fails because the recursive properties are not allowed.
The Swift compiler reports: "Value type 'FamilyTree.Person' cannot have a stored property that recursively contains it"
{
"familyTree": {
"rootPerson": {
"name": "Tom",
"parents": {
"mother": {
"name": "Ma",
"parents": {
"mother": {
"name": "GraMa",
"parents": {}
},
"father": {
"name": "GraPa",
"parents": {}
}
}
},
"father": {
"name": "Pa",
"parents": {}
}
}
}
}
}
Ideally the end result would be a bunch of person objects pointing to their mother and father objects starting from a rootPerson object.
The first thought is to create structs such as:
struct Person: Codable {
var name: String
var parents: Parents
}
struct Parents: Codable {
var mother: Person?
var father: Person?
}
But this doesn't work since you can't have recursive stored properties like this.
Here is one possible working solution:
let json = """
{
"familyTree": {
"rootPerson": {
"name": "Tom",
"parents": {
"mother": {
"name": "Ma",
"parents": {
"mother": {
"name": "GraMa",
"parents": {}
},
"father": {
"name": "GraPa",
"parents": {}
}
}
},
"father": {
"name": "Pa",
"parents": {}
}
}
}
}
}
"""
struct Person: Codable {
var name: String
var parents: [String: Person]
}
struct FamilyTree: Codable {
var rootPerson: Person
}
struct Root: Codable {
var familyTree: FamilyTree
}
let decoder = JSONDecoder()
let tree = try decoder.decode(Root.self, from: json.data(using: .utf8)!)
print(tree)
In a playground this will correctly parse the JSON.
The parents dictionary of Person will have keys such as "mother" and "father". This supports a person have any number of parents with any role.
Possible implementation using classes.
(Swift 5 synthesizes default initializers for classes. Dont remember if so for Swift 4)
import Foundation
var json: String = """
{
"familyTree": {
"rootPerson": {
"name": "Tom",
"parents": {
"mother": {
"name": "Ma",
"parents": {
"mother": {
"name": "GraMa",
"parents": {}
},
"father": {
"name": "GraPa",
"parents": {}
}
}
},
"father": {
"name": "Pa",
"parents": {}
}
}
}
}
}
"""
final class Parents: Codable{
let mother: Person?
let father: Person?
}
final class Person: Codable{
let name: String
let parents: Parents?
}
final class RootPerson: Codable {
var rootPerson: Person
}
final class Root: Codable {
var familyTree: RootPerson
}
var jsonData = json.data(using: .utf8)!
do{
let familyTree = try JSONDecoder().decode(Root.self, from: jsonData)
print("Ma •••>", familyTree.familyTree.rootPerson.parents?.mother?.name)
print("GraPa •••>", familyTree.familyTree.rootPerson.parents?.mother?.parents?.father?.name)
print("Shoud be empty •••>", familyTree.familyTree.rootPerson.parents?.mother?.parents?.father?.parents?.father?.name)
} catch {
print(error)
}

Swift Codable protocol with recursive enums to encode JSON

I'm new to Swift Codable and I trying to encode a "complex" JSON structure from myself. It is a little difficult, because it needs to be recursive.
I found an solution on stack overflow for revusive enums, but it I fail to implement it for my JSON in Swift. (Swift Codable protocol with recursive enums) I got a solution for python but in Swift is much more complicated.
The JSON file would be something like this:
[
{
"id": 0,
"name": "Simple Rule A",
"operations": {
"attribute": "C",
"value": false
}
},
{
"id": 1,
"name": "Simple Rule A",
"operations": {
"ruleOperator": "AND",
"ruleOperand":[
{
"attribute": "A",
"value": false
},
{
"attribute": "C",
"value": false
}
]
}
},
{
"id": 2,
"name": "Simple Rule B",
"operations": {
"ruleOperator": "AND",
"ruleOperand":[
{"ruleOperator": "OR",
"ruleOperand":[
{
"attribute": "A",
"value": false
},
{
"attribute": "C",
"value": false
}
]
},
{
"attribute": "C",
"value": false
}
]
}
}
]
It is a array with Rules and every rule has an id and name and there operations. The operations can be a "node" with an Operator and Operands or "leafs" as an Operand with attribute and value.
Thats what I got:
import Foundation
struct Rule: Decodable {
let id: Int
let name: String
let operations: Operations
}
struct Operations {
//var ruleOperator: String
var ruleOperator: String?
var kind: Kind?
enum Kind {
case node([Operations])
case leaf(Operand)
}
init(name: String, ruleOp: String, kind: Kind) {
self.ruleOperator = ruleOp
self.kind = kind
}
}
extension Operations: Decodable {
enum CodingKeys: String, CodingKey {
case name
case ruleOperator
case nodes
case test
}
enum CodableError: Error {
case decoding(String)
case encoding(String)
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let someRuleOperator = try? container.decode(String.self, forKey: .ruleOperator) {
self.ruleOperator = someRuleOperator
}
if let someOperand = try container.decodeIfPresent(Operand.self, forKey: .nodes) {
self.kind = .leaf(someOperand)
}
if let array = try? container.decode([Operations].self, forKey: .nodes) {
self.kind = .node(array)
}
return
}
}
struct Operand: Decodable {
let attribute: String
let value: Bool
}
enum Operator {
case IS
case AND
case OR
case XOR
}
The Problem is the "init(from decoder: Decoder)" part. Also the "ruleOperator: String?" as an Optional is crap.
Maybe some one can help me :) and give me a hint in the right direction.
(Also nice would be help how I can use the Operator enum instead of the string for e.g. "IS" or "AND".)
Solution
Rework the JSON structure and use this solution Swift Codable protocol with recursive enums
{
"id": 1,
"name": "Simple Rule A",
"operations": [
{
"attribute": "AND",
"value": null,
"operations": [
{
"attribute": "A",
"value": true,
"operations": []
},
{
"attribute": "B",
"value": true,
"operations": []
}
]
}
]
}

Swift 4: how to implement codable protocol for json array of different types

Can somebody enlighten me how to implement codable protocol to swift structs, for the following json array of multiple types?
In the following json array of materials, it can be reflection object, video or note objects.
{
"materials": [
{
"reflection": {
"title": "3-2-1 reflection",
"description": "please reflect after today",
"questions": [
{
"question": "question 1",
"answer": "answer 1",
"answerImageUrl": "http://xxx"
},
{
"question": "question 1",
"answer": "answer 1",
"answerImageUrl": "http://xxx"
}
]
}
},
{
"video": {
"title": "the jungle",
"description": "please watch after today lesson",
"videoUrl": "http://"
}
},
{
"note": {
"title": "the note",
"description": "please read after today lesson"
}
}
]
}
If you can live with a Material that has three optional properties, this is quite straightforward:
struct Response: Codable {
let materials: [Material]
}
struct Material: Codable {
let reflection: Reflection?
let video: Video?
let note: Note?
}
struct Video: Codable {
let title, description, videoUrl: String
}
struct Reflection: Codable {
let title, description: String
let questions: [Question]
}
struct Question: Codable {
let question, answer, answerImageUrl: String
}
struct Note: Codable {
let title, description: String
}
let response = JSONDecoder().decode(Response.self, from: data)