ObjectMapper how to map different JSON based objects

I am using ObjectMapper ( https://github.com/Hearst-DD/ObjectMapper ) to map my JSON to Swift objects.

Let's say I have this JSON structure:

{
  animals: [
    {
      "type": "Cat",
      "weight": 23,
      "catchMice": true
    },
    {
      "type": "Fish",
      "weight": 1,
      "swim": true
    }
  ]
}

      

I have the following Swift objects:

class Foo: Mappable {
  var animals: [Animal] = []

  func mapping(map: Map) {
    animals <- map["animals"] //But I want to be able to distinguish between Cat and Fish objects here
  }
}

class Animal: Mappable {
  var type: String?
  var weight: Double?

  required init?(map: Map) {}

  func mapping(map: Map) {
    type <- map["type"]
    weight <- map["weight"]
  }
}

class Cat: Animal { // How do I make use of this class
  var catchMice: Bool?
}

class Fish: Animal { // How do I make use of this class 
  var swim: Bool?
}

      

How can I distinguish between Cat

and Fish

in my display using the key type

in my JSON objects? Thank!

+6
json swift objectmapper


source to share


4 answers


the details

  • Xcode 10.2.1 (10E1001), Swift 5

JSON file

{
    "animals": [
        {
            "id": 1,
            "name": "Cat",
            "type": "cat",
            "weight": 23,
            "area": ["home", "street"],
            "can_climb_trees": true,
            "competence": [
                { "id": 1, "name": "to catch mouse" },
                { "id": 2, "name": "to mew" },
                { "id": 3, "name": "to wake people up in the morning" },
                { "id": 4, "name": "to eat fish" }
            ]
        },
        {
            "id": 2,
            "name": "fish",
            "type": "fish",
            "weight": 1,
            "area": ["ocean", "lake"],
            "can_swim": false,
            "competence": [
                { "id": 5, "name": "to swim" },
                { "id": 6, "name": "to tease a cat" }
            ]
        },
        {
            "id": 3,
            "name": "dog",
            "weight": 55,
            "area": ["house", "street"],
            "competence": [
                { "id": 5, "name": "to bring newspaper" },
                { "id": 6, "name": "to a good booy" }
            ]
        },
        {
            "id": 4,
            "name": "Cat",
            "type": "cat",
            "weight": 23,
            "area": ["home", "street"],
            "can_climb_trees": true,
            "competence": [
                { "id": 1, "name": "to catch mouse" },
                { "id": 2, "name": "to mew" },
                { "id": 3, "name": "to wake people up in the morning" },
                { "id": 4, "name": "to eat fish" }
            ]
        }
    ]
}

      


ObjectMapper Sample

Detect objects in an array

import Foundation
import ObjectMapper

class AnimalsArrayTransformType: TransformType {

    public typealias Object = [Animal]
    public typealias JSON = [[String: Any]]

    func transformToJSON(_ value: [Animal]?) -> [[String : Any]]? {
        guard let animals = value else { return nil }
        return animals.map { $0.toJSON() }
    }

    func transformFromJSON(_ value: Any?) -> [Animal]? {
        guard let animals = value as? [[String: Any]] else { return nil }
        return animals.compactMap { dictionary -> Animal? in
            if let cat = Cat(JSON: dictionary) { return cat }
            if let fish = Fish(JSON: dictionary) { return fish }
            if let animal = Animal(JSON: dictionary) { return animal }
            return nil
        }
    }
}

      

Mapping classes

import Foundation
import ObjectMapper

class Animals: Mappable, CustomStringConvertible {
    private(set) var animals: [Animal] = []
    required init?(map: Map) { }

    func mapping(map: Map) {
        animals <- (map["animals"], AnimalsArrayTransformType())
    }
}

class BaseObject: Mappable, CustomStringConvertible {
    private(set) var id: Int?
    private(set) var name: String?

    required init?(map: Map) { mapping(map: map) }

    func mapping(map: Map) {
        id <- map["id"]
        name <- map["name"]
    }
}

class Animal: BaseObject {
    private(set) var type: String?
    private(set) var weight: Double?
    private(set) var area: [String]?
    private(set) var competence: [BaseObject]?

    required init?(map: Map) { super.init(map: map) }

    override func mapping(map: Map) {
        super.mapping(map: map)
        type <- map["type"]
        weight <- map["weight"]
        area <- map["area"]
        competence <- map["competence"]
    }
}

class Cat: Animal {
    private(set) var canClimbTrees: Bool?

    required init?(map: Map) {
        super.init(map: map)
        if canClimbTrees == nil { return nil }
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        canClimbTrees <- map["can_climb_trees"]
    }
}

class Fish: Animal {
    private(set) var canSwim: Bool?

    required init?(map: Map) {
        super.init(map: map)
        if canSwim == nil { return nil }
    }

    override func mapping(map: Map) {
        super.mapping(map: map)
        canSwim <- map["can_swim"]
    }
}

      

Helpers

extension Mappable {
    var description: String {
        return toJSONString(prettyPrint: true) ?? "\(self)"
    }
}

      

Usage (read JSON from file)

    func sample() {
        if let path = Bundle.main.path(forResource: "data", ofType: "json") {
            do {
                let text = try String(contentsOfFile: path, encoding: .utf8)
                if let dict = try JSONSerialization.jsonObject(with: text.data(using: .utf8)!, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] {
                    if let data = Animals(JSON: dict) {
                        print(data.animals.map {"class: \(type(of: $0))" }.joined(separator: ", ") )
                        // class: Cat, class: Fish, class: Animal
                        print("===============\n\(data)")
                    }
                }
            }catch {
                print("\(error.localizedDescription)")
            }
        }
    }

      




Sample to be encoded

Detect objects in an array

class Animals: Codable {

    fileprivate enum CodingKeys: String, CodingKey {
        case animals
    }

    private(set) var animals: [Animal]

    required init(from decoder: Decoder) throws {
        self.animals = []
        let container = try decoder.container(keyedBy: CodingKeys.self)
        var unkeyedDecodingContainer = try container.nestedUnkeyedContainer(forKey: .animals)
        while !unkeyedDecodingContainer.isAtEnd {
            if let obj = try? unkeyedDecodingContainer.decode(Cat.self) {
                animals.append(obj)
                continue
            }

            if let obj = try? unkeyedDecodingContainer.decode(Fish.self) {
                animals.append(obj)
                continue
            }

            if let obj = try? unkeyedDecodingContainer.decode(Animal.self) {
                animals.append(obj)
                continue
            }
        }
    }
}

      

Mapping classes

enum AnimalType: String, Codable {
    case cat = "cat", fish = "fish"
}

class BaseObject: Codable {
    private(set) var id: Int?
    private(set) var name: String?
}

class Animal: BaseObject {
    private(set) var type: AnimalType?
    private(set) var weight: Int?
    private(set) var area: [String]?
    private(set) var competence: [BaseObject]?

    private enum CodingKeys: String, CodingKey {
        case type, weight, area, competence
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeIfPresent(type, forKey: .type)
        try container.encodeIfPresent(weight, forKey: .weight)
        try container.encodeIfPresent(area, forKey: .area)
        try container.encodeIfPresent(competence, forKey: .competence)
        try super.encode(to: encoder)
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        type = try container.decodeIfPresent(AnimalType.self, forKey: .type)
        weight = try container.decodeIfPresent(Int.self, forKey: .weight)
        area = try container.decodeIfPresent([String].self, forKey:  .area)
        competence = try container.decodeIfPresent([BaseObject].self, forKey: .competence)
        try super.init(from: decoder)
    }
}

class Cat: Animal {
    private(set) var canClimbTrees: Bool

    private enum CodingKeys: String, CodingKey {
        case canClimbTrees = "can_climb_trees"
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeIfPresent(canClimbTrees, forKey: .canClimbTrees)
        try super.encode(to: encoder)
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.canClimbTrees = try container.decode(Bool.self, forKey: .canClimbTrees)
        try super.init(from: decoder)
    }
}


class Fish: Animal {

    private(set) var canSwim: Bool

    enum CodingKeys: String, CaseIterable, CodingKey {
        case canSwim = "can_swim"
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeIfPresent(canSwim, forKey: .canSwim)
        try super.encode(to: encoder)
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.canSwim = try container.decode(Bool.self, forKey: .canSwim)
        try super.init(from: decoder)
    }
}

      

Helpers

extension Decodable where Self : Encodable {

    dynamic func format(options: JSONEncoder.OutputFormatting) -> String {
        let encoder = JSONEncoder()
        encoder.outputFormatting = options
        do {
            let jsonData = try encoder.encode(self)
            if let jsonString = String(data: jsonData, encoding: .utf8) { return "\(jsonString)" }
        } catch {
            print("\(error.localizedDescription)")
        }
        return "nil"
    }
}

      

Usage (read JSON from file)

func sample() {
    if let path = Bundle.main.path(forResource: "data", ofType: "json") {
        do {
            guard let data = try String(contentsOfFile: path, encoding: .utf8).data(using: .utf8) else { return }
            let decoder = JSONDecoder()
            let result = try decoder.decode(Animals.self, from: data)
            print(result.animals.map {"\(type(of: $0))" } )
            //print("===============")
            //print(result.format(options: .prettyPrinted))
        } catch let error {
            print("\(error.localizedDescription)")
        }
    }
}

      


Output

["Cat", "Fish", "Animal", "Cat"]

+13


source to share


My solution is using the ObjectMapper library for fast.

If you have this:

{
    "animals": [{
            "type": "Cat",
            "weight": 23,
            "catchMice": true
        },
        {
            "type": "Fish",
            "weight": 1,
            "swim": true
        }
    ]
}

      

This website returns your complete object



import Foundation 
import ObjectMapper 

class Main: Mappable { 

    var animals: [Animals]? 

    required init?(map: Map){ 
    } 

    func mapping(map: Map) {
        animals <- map["animals"] 
    }
} 

class Animals: Mappable { 

    var type: String? 
    var weight: NSNumber? 
    var catchMice: Bool? 

    required init?(map: Map){ 
    } 

    func mapping(map: Map) {
        type <- map["type"] 
        weight <- map["weight"] 
        catchMice <- map["catchMice"] 
    }
} 

      

I have work on this project which helps to create your displayables for ObjectMappable (swift) library.

https://github.com/andreycattalin/JSONtoSwiftObjectMapper

Current version: http://izee.ro/andrey/JSONtoSwiftObjectMapper/

+1


source to share


Convert your JSON string to an array first, then loop through each dictionary in the array, check the type value and then select the model for public func map(JSON: [String: Any]) -> N?

0


source to share


        Simple example as follow
        //
        //  ViewController.swift
        //  TriyalJSON
        //
        //  Created by Mac on 19/02/19.
        //  Copyright © 2019 shital. All rights reserved.
        //

        import UIKit
        class ViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
            @IBOutlet weak var mytable: UITableView!
            var arrmain = [GETArrayData]()
            override func viewDidLoad() {
                super.viewDidLoad()
                getdata()
                // Do any additional setup after loading the view, typically from a nib.
            }

            override func didReceiveMemoryWarning() {
                super.didReceiveMemoryWarning()
                // Dispose of any resources that can be recreated.
            }
        func getdata()
        {
            let dict = [
                "name":"shital",
                "address":"pune"
            ]
            APIManager.sharedInstance.getdata(parms: dict, onsuccess: { (responsedata, anystring) in
                print(responsedata,anystring)
                self.arrmain = responsedata
                let str = self.arrmain[0]
                print("Email-",str.email)
                self.mytable.reloadData()
            }) { (error1, error2) in
                print(error1,error2)
            }
            }
            func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                return self.arrmain.count
            }
            func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                        let cell:UITableViewCell = mytable.dequeueReusableCell(withIdentifier: "TableViewCell") as! UITableViewCell
                let str1 = self.arrmain[indexPath.row]
                cell.textLabel?.text = str1.email

                return cell

            }
            func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
                return 400.0
            }
            func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                let obj = storyboard?.instantiateViewController(withIdentifier: "ViewControllerFrist") as! ViewControllerFrist
        //        ViewControllerFrist.arrpass = self.arrmain
                var strpass = self.arrmain[indexPath.row]
                obj.arrpass = [strpass]
           // obj.arrpass = self.arrmain

                self.navigationController? .pushViewController(obj, animated: true)
            }
        }
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------

        //
        //  ViewControllerFrist.swift
        //  TriyalJSON
        //
        //  Created by Mac on 20/02/19.
        //  Copyright © 2019 shital. All rights reserved.
        //

        import UIKit

        class ViewControllerFrist: UIViewController,UITableViewDelegate,UITableViewDataSource {
            var arrpass = [GETArrayData]()
            static let fristobject = ViewControllerFrist()
            @IBOutlet weak var mytableView: UITableView!
            override func viewDidLoad() {
                super.viewDidLoad()

                // Do any additional setup after loading the view.
            }

            override func didReceiveMemoryWarning() {
                super.didReceiveMemoryWarning()
                // Dispose of any resources that can be recreated.
            }
            func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                print(self.arrpass.count)
                return self.arrpass.count

            }
            func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                let cell:TableViewCell = mytableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
                let str = self.arrpass[indexPath.row]
                cell.textLabel1.text = str.email
                cell.textLabel2.text = "\(str.id)"

                return cell
            }
            func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
                return 300.0
            }

        }

        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------
        ------------------------------


        //
        //  APIManager.swift
        //  TriyalJSON
        //
        //  Created by Mac on 19/02/19.
        //  Copyright © 2019 shital. All rights reserved.
        //

        import UIKit
        import Foundation
        import Alamofire
        import ObjectMapper
        class APIManager: NSObject {

            static let sharedInstance = APIManager()

            func getdata(parms:[String:Any], onsuccess:@escaping([GETArrayData],String?) ->Void,onfailure:@escaping(String?,String?) ->Void)
            {
                let url = URL.init(string: "https://jsonplaceholder.typicode.com/comments")

                let headrs : HTTPHeaders = ["content-type":"application/json"]
                Alamofire.request(url!, method: .get, parameters: parms, encoding: JSONEncoding.default, headers: headrs).responseJSON
                    {
                        response in
                        switch response.result{
                        case .success:

        //                    let string = NSString(data: response.data!, encoding: String.Encoding.utf8.rawValue)
        //                    print("string:\(String(describing: string))")
                            do{
                                let jsonResponse = try JSONSerialization.jsonObject(with: response.data!, options: []) as! AnyObject
                                //let userdetails = Mapper<DataClasss>() .mapArray(JSONObject: jsonResponse)
        //                        let userdetalis = Mapper<GETArrayData>() .mapArray(JSONString: jsonResponse)
                                let userdetails = Mapper<GETArrayData>() .mapArray(JSONObject: jsonResponse)
                                print(jsonResponse)
                                if jsonResponse != nil
                                {
                                    onsuccess(userdetails!,"success")
                                }
                                else
                                {
                                    onfailure((response.error?.localizedDescription)!,"fail")
                                }

                            }catch let parsingError{

                                print("error",parsingError)
                                onfailure((response.error?.localizedDescription)!,"fail")
                            }
                            break
                        case.failure(let error):

                            print(error)
                            onfailure((response.error?.localizedDescription)!,"fail")
                        }

                }
            } 


        }


    --------------------
    --------------------
    --------------------
    --------------------
    --------------------
    --------------------
    //
    //  SecendViewController.swift
    //  ITGURUassignment
    //
    //  Created by Mac on 18/02/19.
    //  Copyright © 2019 shital. All rights reserved.
    //

    import UIKit

    class SecendViewController: UIViewController {


        @IBOutlet weak var textName: UITextField!

        @IBOutlet weak var txtEmail: UITextField!

        @IBOutlet weak var txtFeatureTitle: UITextField!

        @IBOutlet weak var txtFeatureDescription: UITextView!


        @IBOutlet weak var txtUseCase: UITextView!

        @IBOutlet weak var btnlow: UIButton!



        var strresult = ""


        @IBAction func sendRequestPressed(_ sender: UIButton) {
           var strname = self.textName.text!
            var stremail = self.txtEmail.text!
            var strtitle = self.txtFeatureTitle.text!

            if strname.count <= 0
           {
            print("Enter Frist Name")
            }

           else if stremail.count <= 0 {
                print("enter last name")

            }
            else if strtitle.count <= 0 {
                print("Enter feature title")

            }
            else if self.strresult.count <= 0
            {
                print("Button  not selected:\(strresult)")
            }
            else
            {
                print("Button  selected:\(strresult)")
                let dict = [
                    "AppID":"67-5555545ete",
                    "FeatureTitle":"\(self.txtFeatureTitle.text!)",
                    "UserName":"laura",
                    "UserEmail":"\(self.txtEmail.text!)",
                    "Priority":self.strresult,
                    "Description":"\(self.txtFeatureDescription.text ?? "")",
                    "UseCase":"\(self.txtUseCase.text ?? "")",
                    "DeviceType":"iphone"
                ]
                print(dict)
            }



        }


        @IBAction func btnhighpressed(_ sender: UIButton) {
            self.strresult = "H"
            print(strresult)
            self.btnhigh.setImage(imgselected, for: .normal)
            self.btnlow.setImage(imgunselected, for: .normal)
            self.btnmedium.setImage(imgunselected, for: .normal)
        }


        @IBAction func btnlowpressed(_ sender: UIButton) {
            self.strresult = "L"
            print(strresult)
            self.btnhigh.setImage(imgunselected, for: .normal)
            self.btnlow.setImage(imgselected, for: .normal)
            self.btnmedium.setImage(imgunselected, for: .normal)
        }


        @IBAction func btnmedium(_ sender: UIButton) {
            self.strresult = "M"
            print(strresult)
            self.btnhigh.setImage(imgunselected, for: .normal)
            self.btnlow.setImage(imgunselected, for: .normal)
            self.btnmedium.setImage(imgselected, for: .normal)
        }


        @IBOutlet weak var btnmedium: UIButton!

        @IBOutlet weak var btnhigh: UIButton!

        let imgselected = UIImage.init(named: "Selected")

        let imgunselected = UIImage.init(named: "Unselected")

        override func viewDidLoad() {
            super.viewDidLoad()

            self.btnhigh.setImage(imgunselected, for: .normal)

            self.btnlow.setImage(imgunselected, for: .normal)

            self.btnmedium.setImage(imgunselected, for: .normal)
            // Do any additional setup after loading the view.
        }

        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }

    }

-----------------
-----------------
-----------------
-----------------
-----------------
----------------
----------------
//
//  ViewController.swift
//  ITGURUassignment
//
//  Created by Mac on 18/02/19.
//  Copyright © 2019 shital. All rights reserved.
//

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {

//    var arrCountryList = [CountryDetails]()
    var arruserdetalis = [USERdetalis]()
//    var usermasterdetails = USERdetalis()

    @IBAction func featureRequest(_ sender: UIButton) {
        let objectforsecviewcontroller = storyboard?.instantiateViewController(withIdentifier: "SecendViewController") as! SecendViewController
        self.navigationController?.pushViewController(objectforsecviewcontroller, animated: true)

    }
    @IBOutlet weak var getdetalitable: UITableView!
    //    @IBAction func nextbtn(_ sender: UIButton) {
//        let vc = storyboard? .instantiateViewController(withIdentifier: "SecendViewController") as! SecendViewController
//        self.navigationController?.pushViewController(vc, animated: true)
//
//    }


    override func viewDidLoad() {
        super.viewDidLoad()
        getdata()

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
func getdata()
{
    let dict = [
        "name":"shital",
        "roll":"one"
    ]
    APIManager.sharedInstance.getuserdetalis(parms: dict, onsuccess: { (arruserdetalis, anystring) in
        print(arruserdetalis,anystring)
        self.arruserdetalis = arruserdetalis
        var str = arruserdetalis[0]
        self.getdetalitable.reloadData()
    }) { (error1, error2) in
        print(error1,error2)
    }

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.arruserdetalis.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell:TableViewCell = getdetalitable.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell


        let userdetalis = self.arruserdetalis[indexPath.row]


        cell.textLabel?.text = userdetalis.title ?? ""


        if(userdetalis.isSelected == true)
        {
            cell.btnVote.setTitle("Voted", for: .normal)
            cell.btnVote.isEnabled = false
        }
        else {

            cell.btnVote.setTitle("Vote your vote", for: .normal)
        }

        cell.btnVote.tag = indexPath.row

        cell.btnVote.addTarget(self, action: #selector(voteButtonPressed(sender:)), for: .touchUpInside)


        return cell
    }

    @objc func voteButtonPressed(sender:UIButton){

        let userdetalis = self.arruserdetalis[sender.tag]


        self.getdetalitable.reloadData()


        let dict = [
            "commandtype":"registervote",
            "RequestID":userdetalis.id,
            "usename":userdetalis.title

            ] as [String : Any]

        APIManager.sharedInstance.voteApi(parms: dict, onsuccess: { (response, anystring) in
            print(response,anystring)
            self.getdata()
        }) { (error1, error2) in
            print(error1,error2)
        }



    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 500.0
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


        let third = storyboard?.instantiateViewController(withIdentifier: "ThirdViewController") as! ThirdViewController
       self.navigationController? .pushViewController(third, animated: true)
    }
}

      

0


source to share







All Articles
Loading...
X
Show
Funny
Dev
Pics