0

I have a JSON response (below) and I need to parse this -

[
{
    "id":123,
    "name":"Fahim Rahman",
    "age":25,
    "friends":[
        {
            "firstName": "Imtiaz",
            "lastName": "Khan",
            "avatar_url": null
        }
    ],
    "groups":{
        "xcet":{
            "name":"xcek cert etsh tnhg",
            "createdDate":"2022-10-31T10:00:48Z"
        },
        "juyt":{
            "name":"jfd uyt you to",
            "createdDate":"2021-09-13T10:00:00Z"
        },
        "some random key":{
            "name": "some name",
            "createdDate":"2026-03-27T10:00:00Z"
        }
    }
}
]

To parse this in my code I've created this model. I can not able to parse the groups as that is not a list but an object -

    import ObjectMapper
    
    class Person: BaseObject {
        @objc dynamic var ID: Int = -1
        @objc dynamic var name: String = ""
        @objc dynamic var age: Int = -1
        
        var friendsList = List<Friends>()
     
        override func mapping(map: ObjectMapper.Map) {
            ID <- map["id"]
            name <- map["name"]
            age <- map["age"]
            friendsList <- map["friends"]
        }
    }

    class Friends: BaseObject {
        @objc dynamic var firstName: String = ""
        @objc dynamic var lastName: String = ""
        @objc dynamic var avatarURL: String = ""
   
        override func mapping(map: ObjectMapper.Map) {
            firstName <- map["firstName"]
            lastName <- map["name"]
            avatarURL <- map["avatar_url"]
        }
    }    

I know it's a bad JSON. The groups should be on the list instead of the nested objects but unfortunately, I'm getting this response.

Here in the response of groups, the number of nested objects is dynamic and the key of the nested object is also dynamic. Thus I can not able to parse this as friends attribute.

So my question is, how can I map the "groups"?

1
  • 1
    Libraries like ObjectMapper and SwiftyJSON have become obsolete since Swift 4 when Codable was introduced. groups can be decoded as [String:Group] or with a custom decoding strategy even as array. Commented Nov 1, 2022 at 7:26

4 Answers 4

1

Before mapping groups, we need a class that can hold each Group alongside its key (i.e. xct)

For example

Class Groups: BaseObject {
@objc dynamic var key: String = ""
@objc dynamic var value: GroupsItem?


convenience init(key: String, value: GroupsItem) {
    self.init()
    self.key = key
    self.value = value
}    

}

Class GroupsItem: BaseObject {
 @objc dynamic var name: String?
 @objc dynamic var createdDate: String?
...
}

Then inside your Person class you can map this as -

private func mapGroupsItems(map: ObjectMapper.Map) -> List<GroupsItem> {
var rowsDictionary: [String: Groups]?
    rowsDictionary <- map["groups"]
    let rows = List<GroupsItem>()
    if let dictionary = rowsDictionary {
        for (key, value) in dictionary {
            rows.append(GroupsItem(key: key, value: value))
        }
    }
    return rows

}

dont forget to call this method from mapping -

override public func mapping(map: ObjectMapper.Map) {
...
groups = mapGroupsItems(map: map)
}
Sign up to request clarification or add additional context in comments.

Comments

1

try this approach, using a custom init(from decoder: Decoder) for Groups, works well for me. Use a similar approach for non-SwiftUI systems.

struct ContentView: View {
    
    @State var people: [Person] = []
    
    var body: some View {
        ForEach(people) { person in
            Text(person.name)
            ForEach(Array(person.groups.data.keys), id: \.self) { key in
                Text(key).foregroundColor(.red)
                Text(person.groups.data[key]?.name ?? "no name").foregroundColor(.blue)
                Text(person.groups.data[key]?.createdDate ?? "no date").foregroundColor(.blue)
            }
        }
        .onAppear {
                let json = """
[
{
    "id":123,
    "name":"Fahim Rahman",
    "age":25,
    "friends":[
        {
            "firstName": "Imtiaz",
            "lastName": "Khan",
            "avatar_url": null
        }
    ],
    "groups":{
        "xcet":{
            "name":"xcek cert etsh tnhg",
            "createdDate":"2022-10-31T10:00:48Z"
        },
        "juyt":{
            "name":"jfd uyt you to",
            "createdDate":"2021-09-13T10:00:00Z"
        },
        "some random key":{
            "name": "some name",
            "createdDate":"2026-03-27T10:00:00Z"
        }
    }
}
]
"""
                if let data = json.data(using: .utf8) {
                    do {
                        self.people = try JSONDecoder().decode([Person].self, from: data)
                        print("---> people: \(people)")
                    } catch {
                        print("decode error: \(error)")
                    }
                }
                
            }
    }
    
}

struct Person: Identifiable, Codable {
    let id: Int
    var name: String
    var age: Int
    var friends: [Friend]
    var groups: Groups
}

struct Friend: Codable {
    var firstName, lastName: String
    var avatarURL: String?
    
    enum CodingKeys: String, CodingKey {
        case firstName, lastName
        case avatarURL = "avatar_url"
    }
}

struct Info: Codable {
    var name: String
    var createdDate: String
}

struct Groups: Identifiable, Codable {
    let id = UUID()
    var data: [String:Info] = [:]

    init(from decoder: Decoder) throws {
        do {
            let container = try decoder.singleValueContainer()
            self.data = try container.decode([String:Info].self)
        } catch {
            print(error)
        }
    }
    
}

Comments

0

Your Model classes structure will be

// MARK: - Welcome7Element
struct Welcome7Element {
    let id: Int
    let name: String
    let age: Int
    let friends: [Friend]
    let groups: Groups
}

// MARK: - Friend
struct Friend {
    let firstName, lastName: String
    let avatarURL: NSNull
}

// MARK: - Groups
struct Groups {
    let xcet, juyt, someRandomKey: Juyt
}

// MARK: - Juyt
struct Juyt {
    let name: String
    let createdDate: Date
}

1 Comment

hey @Ahsan, the number of objects in Groups is dynamic like there can be 100 nested objects present. In that case, how can that scenario be handled by this Groups struct?
0

Thank you @shakif_ for your insightful answer. Here is how I solved this based on that answer -

import ObjectMapper
import RealmSwift

class Person: BaseObject {
    @objc dynamic var ID: Int = -1
    @objc dynamic var name: String = ""
    @objc dynamic var age: Int = -1
    
    var friendsList = List<Friends>()
    var groups = List<Group>
 
    override func mapping(map: ObjectMapper.Map) {
        ID <- map["id"]
        name <- map["name"]
        age <- map["age"]
        friendsList <- map["friends"]
        groups = extractGroups(map)
    }

    private func extractGroups(_ map: ObjectMapper.Map) -> List<Group> {
         
         let items = List<Group>()

         var modifiedJSON = [String: Group]
         modifiedJSON <- map["groups"]

         for (key,value) in modifiedJSON {
             let item = GroupMapper(key: key, value: value)
             if let group = item.value {
                 items.append(group)
             }
         }

         return items
    }

}

class Friends: BaseObject {
    @objc dynamic var firstName: String = ""
    @objc dynamic var lastName: String = ""
    @objc dynamic var avatarURL: String = ""

    override func mapping(map: ObjectMapper.Map) {
        firstName <- map["firstName"]
        lastName <- map["name"]
        avatarURL <- map["avatar_url"]
    }
}   

class Group: BaseObject {
    @objc dynamic var name: String = ""
    @objc dynamic var createdDate: String = ""

    override func mapping(map: ObjectMapper.Map) {
        name <- map["name"]
        createdDate <- map["createdDate"]
    }
} 

struct GroupMapper {
    var key: String = ""
    var value: Group?
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.