web-dev-qa-db-ja.com

ObjectMapper JSONに基づいて異なるオブジェクトをマップする方法

ObjectMapper( https://github.com/Hearst-DD/ObjectMapper )を使用して、JSONをSwiftオブジェクトにマップしています。

次のJSON構造があるとします。

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

次のSwiftオブジェクトがあります:

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?
}

JSONオブジェクトのCatキーを使用して、マッピングでFishtypeをどのように区別できますか?本当にありがとう!

8
7ball

細部

  • Xcode 10.2.1(10E1001)、Swift 5

jsonファイル

{
    "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サンプル

配列内のオブジェクトを検出する

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
        }
    }
}

クラスのマッピング

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"]
    }
}

ヘルパー

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

使用法(ファイルからjsonを読み取る)

    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)")
            }
        }
    }

コード化可能なサンプル

配列内のオブジェクトを検出する

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
            }
        }
    }
}

クラスのマッピング

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)
    }
}

ヘルパー

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"
    }
}

使用法(ファイルからjsonを読み取る)

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)")
        }
    }
}

出力

[「猫」、「魚」、「動物」、「猫」]

14

私のソリューションは、SwiftのObjectMapperライブラリを使用しています。

これがあれば:

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

このウェブサイトはあなたの完全なオブジェクトを返します

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"] 
    }
} 

'ObjectMappable'ライブラリ(Swift)のマップ可能なオブジェクトの作成に役立つこのプロジェクトに取り組んでいます。

https://github.com/andreycattalin/JSONtoSwiftObjectMapper

ライブバージョン: http://izee.ro/andrey/JSONtoSwiftObjectMapper/

1
Andrey

最初にJSON文字列を配列に変換してから、配列内の各ディクショナリをループし、タイプの値を調べてから、public func map(JSON: [String: Any]) -> N?に応じてモデルを選択します

0
almas
        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
shital bhosale