我有一个websocket,可以生成不同的json对象。对象不能包含任何公共字段
{
"type": "apple",
"kind": "fruit",
"eatable": true
}
{
"item": "key",
"active": true
}
{
"tool": "screwdriver",
"original": "toolBox",
"cross-head": true
}
我为他们提供了一个类列表(它们可能包含一些逻辑),所以我需要解析它以映射具有某种层次结构的某些模型,例如
如果失败,则尝试解析水果;如果失败,则尝试解析键;尝试解析工具箱。有时我需要添加一些新类来解析现有类的某些对象和一些新字段。
如何组织挑选类进行解析?
更新
答案 0 :(得分:4)
如果该对象中不存在该键,请尝试查找您要查找的键。这应该使您确定适合给定对象的模型类。
使用其他模型类中没有的唯一键
示例:
var array = NSArray(array: [[
"type": "apple",
"kind": "fruit",
"eatable": true
],
[
"item": "key",
"active": true
],
[
"tool": "screwdriver",
"original": "toolBox",
"cross-head": true
]])
for model in array as! [NSDictionary]
{
if(model.value(forKey: "type") != nil)
{
print("use Fruit Model Class")
}
else if(model.value(forKey: "item") != nil)
{
print("use second model class")
}
else
{
print("use third model class")
}
}
答案 1 :(得分:3)
您可以这样做:
首先,您声明您的类型符合Decodable
协议:
struct Fruit : Decodable {
let type : String
let kind : String
let eatable : Bool
}
struct Tool : Decodable {
let tool : String
let original : String
let crossHead : Bool
enum CodingKeys: String, CodingKey {
case tool = "tool"
case original = "original"
case crossHead = "cross-head"
}
}
然后,您扩展Decodable
以“逆向”通用性的使用:
extension Decodable {
static func decode(data : Data, decoder : JSONDecoder = JSONDecoder()) -> Self? {
return try? decoder.decode(Self.self, from: data)
}
}
然后,您扩展JSONDecoder
以便在要测试的类型中尝试可解码的类型:
extension JSONDecoder {
func decode(possibleTypes : [Decodable.Type], from data: Data) -> Any? {
for type in possibleTypes {
if let value = type.decode(data: data, decoder: self) {
return value
}
}
return nil
}
}
最后,您指定要尝试解码的类型:
let decodableTypes : [Decodable.Type] = [Fruit.self, Tool.self]
然后您可以使用它来解码JSON:
let jsonString = """
{
"tool": "screwdriver",
"original": "toolBox",
"cross-head": true
}
"""
let jsonData = jsonString.data(using: .utf8)!
let myUnknownObject = JSONDecoder().decode(possibleTypes: decodableTypes, from: jsonData)
瞧瞧!
现在,您可以在decodableTypes
中添加任意数量的类型,只要它们符合Decodable
协议即可。
这不是最佳方法,因为如果您有很多类型,它并不是最佳选择,但是通过这种方式,您无需在数据中添加区分字段。
答案 2 :(得分:0)
如果所有这些字段都相关或具有联合样式,则可以考虑用户Enum,这也非常易于实现。
let data1 = """
[{
"type": "apple",
"kind": "fruit",
"eatable": true
},
{
"item": "key",
"active": true
},
{
"tool": "screwdriver",
"original": "toolBox",
"cross-head": true
}]
""".data(using: .utf8)!
struct JSONType : Decodable{
let type: String
let kind: String
let eatable : Bool
}
struct JSONItem : Decodable{
let item: String
let active : Bool
}
struct JSONTool : Decodable{
let tool: String
let original : String
let crosshead : Bool
enum CodingKeys: String, CodingKey {
case tool = "tool"
case original = "original"
case crosshead = "cross-head"
}
}
enum JSONData : Decodable{
case type(JSONType)
case item(JSONItem)
case tool(JSONTool)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do{ let temp = try container.decode(JSONType.self); self = .type(temp) ; return}
catch{do { let temp = try container.decode(JSONItem.self) ; self = .item(temp) ; return}
catch{ let temp = try container.decode(JSONTool.self) ; self = .tool(temp) ; return}}
try self.init(from: decoder)
}
func getValue()-> Any{
switch self {
case let .type(x): return x
case let .item(x): return x
case let .tool(x): return x
}
}
}
let result = try JSONDecoder().decode([JSONData].self, from: data1)
print(result[0].getValue())
print (result[1].getValue())
print (result[2].getValue())