ObjectMapper 如何基于 JSON 映射不同的对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43269448/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ObjectMapper how to map different object based on JSON
提问by 7ball
I'm using ObjectMapper (https://github.com/Hearst-DD/ObjectMapper) to map my JSON to Swift objects.
我正在使用 ObjectMapper ( https://github.com/Hearst-DD/ObjectMapper) 将我的 JSON 映射到 Swift 对象。
Say I have this JSON structure:
假设我有这个 JSON 结构:
{
animals: [
{
"type": "Cat",
"weight": 23,
"catchMice": true
},
{
"type": "Fish",
"weight": 1,
"swim": true
}
]
}
I have the following Swift objects:
我有以下 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?
}
How can I distinguish between Catand Fishin my mapping using the typekey in my JSON objects? Thanks so much!
如何使用JSON 对象中的键区分映射Cat和Fish映射type?非常感谢!
回答by Vasily Bodnarchuk
Details
细节
- Xcode 10.2.1 (10E1001), Swift 5
- Xcode 10.2.1 (10E1001),Swift 5
json file
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 sample
对象映射器示例
Detect objects in 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 { 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"]
}
}
.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
映射类
extension Mappable {
var description: String {
return toJSONString(prettyPrint: true) ?? "\(self)"
}
}
Helpers
帮手
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: 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
}
}
}
}
))" }.joined(separator: ", ") )
// class: Cat, class: Fish, class: Animal
print("===============\n\(data)")
}
}
}catch {
print("\(error.localizedDescription)")
}
}
}
Usage (read json from file)
用法(从文件中读取 json)
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)
}
}
Codable sample
可编码样本
Detect objects in array
检测数组中的对象
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"
}
}
Mapping classes
映射类
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: {
"animals": [{
"type": "Cat",
"weight": 23,
"catchMice": true
},
{
"type": "Fish",
"weight": 1,
"swim": true
}
]
}
))" } )
//print("===============")
//print(result.format(options: .prettyPrinted))
} catch let error {
print("\(error.localizedDescription)")
}
}
}
Helpers
帮手
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"]
}
}
Usage (read json from file)
用法(从文件中读取 json)
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)
}
}
Output
输出
["Cat", "Fish", "Animal", "Cat"]
[“猫”、“鱼”、“动物”、“猫”]
回答by Andrey
My solution is using ObjectMapper library for swift.
我的解决方案是快速使用 ObjectMapper 库。
If you have this:
如果你有这个:
##代码##This website returns your complete object
本网站返回您的完整对象
##代码##I have working on this project that helps to create your mappable objects for 'ObjectMappable' library (swift).
我正在研究这个项目,该项目有助于为“ObjectMappable”库(swift)创建可映射对象。
https://github.com/andreycattalin/JSONtoSwiftObjectMapper
https://github.com/andreycattalin/JSONtoSwiftObjectMapper
Live version: http://izee.ro/andrey/JSONtoSwiftObjectMapper/
回答by shital bhosale
回答by almas
Convert your JSON string to array first, then loop through each dictionary in the array, inspect the type value, and then choose the model accordingly for public func map(JSON: [String: Any]) -> N?
首先将您的 JSON 字符串转换为数组,然后遍历数组中的每个字典,检查类型值,然后相应地选择模型 public func map(JSON: [String: Any]) -> N?

