我正在使用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和Fish映射type?非常感谢!
Cat
Fish
type
{ "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" } ] } ] }
检测数组中的对象
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" } }
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)") } } }
[“猫”,“鱼”,“动物”,“猫”]