I have the following data structures in place
我有以下数据结构
struct Symbol : Codable {
let id:Int
let type:String
let properties:[String:String]?
}
struct Event : Codable {
let event:String
let timestamp:Int
let symbol:Symbol?
}
struct LogRow : Codable {
let id:Int
let user_id:String
let question_id:String
let actions:[Event]
let timestamp:Double
}
and the following JSON array
和以下JSON数组
[
{
"id": 26535754,
"user_id": "qhv1i39wsmbkzhjiffk1rrsg",
"question_id": "\"trapdoor|186752c1-948e-4c15-b3df-7d39a99fe9d6\"",
"actions": "[{\"event\": \"OPEN\", \"timestamp\": 1499802241640}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 15, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"timestamp\": 1499802243567}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 15, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"timestamp\": 1499802243567, \"dockingPoint\": \"right\"}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 13, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 15, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"timestamp\": 1499802243699, \"dockingPoint\": \"denominator\"}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 16, \"type\": \"Num\", \"properties\": {\"significand\": \"60\"}}, \"timestamp\": 1499802244570}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 15, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"symbol\": {\"id\": 16, \"type\": \"Num\", \"properties\": {\"significand\": \"60\"}}, \"timestamp\": 1499802244570, \"dockingPoint\": \"argument\"}, {\"event\": \"DROP_SYMBOL\", \"symbol\": {\"id\": 16, \"type\": \"Num\", \"properties\": {\"significand\": \"60\"}}, \"timestamp\": 1499802245281}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 13, \"type\": \"Fraction\"}, \"timestamp\": 1499802245845}, {\"event\": \"TRASH_SYMBOL\", \"symbol\": {\"id\": 13, \"type\": \"Fraction\"}, \"timestamp\": 1499802246826}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 16, \"type\": \"Num\", \"properties\": {\"significand\": \"60\"}}, \"timestamp\": 1499802247468}, {\"event\": \"TRASH_SYMBOL\", \"symbol\": {\"id\": 16, \"type\": \"Num\", \"properties\": {\"significand\": \"60\"}}, \"timestamp\": 1499802248360}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802249161}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802249161, \"dockingPoint\": \"denominator\"}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802249289, \"dockingPoint\": \"denominator\"}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802249797}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802249797, \"dockingPoint\": \"denominator\"}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802249906, \"dockingPoint\": \"denominator\"}, {\"event\": \"DRAG_POTENTIAL_SYMBOL\", \"symbol\": {\"id\": 20, \"type\": \"Num\", \"properties\": {\"significand\": \"4\"}}, \"timestamp\": 1499802251451}, {\"event\": \"DROP_POTENTIAL_SYMBOL\", \"symbol\": {\"id\": 20, \"type\": \"Num\", \"properties\": {\"significand\": \"4\"}}, \"timestamp\": 1499802254229}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802255231}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802255231, \"dockingPoint\": \"denominator\"}, {\"event\": \"TRASH_SYMBOL\", \"symbol\": {\"id\": 19, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802256593}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 20, \"type\": \"Num\", \"properties\": {\"significand\": \"4\"}}, \"timestamp\": 1499802257376}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 14, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 20, \"type\": \"Num\", \"properties\": {\"significand\": \"4\"}}, \"timestamp\": 1499802258062, \"dockingPoint\": \"denominator\"}, {\"event\": \"CLOSE\", \"timestamp\": 1499802259093}]",
"timestamp": 1.499802259277E9
},
{
"id": 26535718,
"user_id": "qhv1i39wsmbkzhjiffk1rrsg",
"question_id": "\"trapdoor|186752c1-948e-4c15-b3df-7d39a99fe9d6\"",
"actions": "[{\"event\": \"OPEN\", \"timestamp\": 1499802175061}, {\"event\": \"DRAG_POTENTIAL_SYMBOL\", \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802178936}, {\"event\": \"DOCK_POTENTIAL_SYMBOL\", \"parent\": {\"id\": 7, \"type\": \"Symbol\", \"properties\": {\"letter\": \"g\"}}, \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802183785, \"dockingPoint\": \"subscript\"}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802184864}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 7, \"type\": \"Symbol\", \"properties\": {\"letter\": \"g\"}}, \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802184865, \"dockingPoint\": \"subscript\"}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 6, \"type\": \"Symbol\", \"properties\": {\"letter\": \"m\"}}, \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802185857, \"dockingPoint\": \"subscript\"}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802186710}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 6, \"type\": \"Symbol\", \"properties\": {\"letter\": \"m\"}}, \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802186710, \"dockingPoint\": \"subscript\"}, {\"event\": \"DROP_SYMBOL\", \"symbol\": {\"id\": 10, \"type\": \"Fraction\"}, \"timestamp\": 1499802188430}, {\"event\": \"DRAG_POTENTIAL_SYMBOL\", \"symbol\": {\"id\": 11, \"type\": \"Fraction\"}, \"timestamp\": 1499802194665}, {\"event\": \"DROP_POTENTIAL_SYMBOL\", \"symbol\": {\"id\": 11, \"type\": \"Fraction\"}, \"timestamp\": 1499802195427}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 6, \"type\": \"Symbol\", \"properties\": {\"letter\": \"m\"}}, \"timestamp\": 1499802196167}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 11, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 6, \"type\": \"Symbol\", \"properties\": {\"letter\": \"m\"}}, \"timestamp\": 1499802197167, \"dockingPoint\": \"numerator\"}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 8, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"timestamp\": 1499802198247}, {\"event\": \"UNDOCK_SYMBOL\", \"parent\": {\"id\": 7, \"type\": \"Symbol\", \"properties\": {\"letter\": \"g\"}}, \"symbol\": {\"id\": 8, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"timestamp\": 1499802198247, \"dockingPoint\": \"right\"}, {\"event\": \"DOCK_SYMBOL\", \"parent\": {\"id\": 11, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 8, \"type\": \"Fn\", \"properties\": {\"name\": \"cos\", \"allowSubscript\": true, \"innerSuperscript\": true}}, \"timestamp\": 1499802199971, \"dockingPoint\": \"right\"}, {\"event\": \"DRAG_START\", \"symbol\": {\"id\": 11, \"type\": \"Fraction\"}, \"timestamp\": 1499802201551}, {\"event\": \"DROP_SYMBOL\", \"symbol\": {\"id\": 11, \"type\": \"Fraction\"}, \"timestamp\": 1499802202638}, {\"event\": \"DRAG_POTENTIAL_SYMBOL\", \"symbol\": {\"id\": 12, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802209025}, {\"event\": \"DOCK_POTENTIAL_SYMBOL\", \"parent\": {\"id\": 11, \"type\": \"Fraction\"}, \"symbol\": {\"id\": 12, \"type\": \"Num\", \"properties\": {\"significand\": \"2\"}}, \"timestamp\": 1499802210771, \"dockingPoint\": \"denominator\"}, {\"event\": \"CLOSE\", \"timestamp\": 1499802212398}]",
"timestamp": 1.49980221259E9
}
]
The JSON data comes from a database, exported via DataGrip's JSON export. The database field is of type JSON Blob (it's Postgres, so it's OK). The query is something like:
JSON数据来自数据库,通过DataGrip的JSON导出导出。数据库字段的类型为JSON Blob(它是Postgres,所以没关系)。查询类似于:
SELECT
...
event_details->>'actions' AS actions,
...
FROM yadda_yadda...;
in case it's useful (yes, I also tried the single arrow version, no difference).
如果它有用(是的,我也试过单箭头版本,没有区别)。
My question is how do I make Swift parse the actions
string as a JSON object into an [Event]
? Alternatively, is there a way of making DataGrip (or postgres) export that field as an object like its container rather than as a serialised object?
我的问题是如何让Swift将动作字符串作为JSON对象解析为[Event]?或者,是否有一种方法可以使DataGrip(或postgres)将该字段导出为像容器一样的对象而不是序列化对象?
EDIT I changed LogRow
like this
编辑我改变了这样的LogRow
struct LogRow : Codable {
let id:Int
let user_id:String
let question_id:String
let actions:[Event]
let timestamp:Double
enum CodingKeys: String, CodingKey {
case id
case user_id
case question_id
case actions
case timestamp
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
user_id = try values.decode(String.self, forKey: .user_id)
question_id = try values.decode(String.self, forKey: .question_id)
timestamp = try values.decode(Double.self, forKey: .timestamp)
let actions_string = try values.decode(String.self, forKey: .actions)
let actions_data = actions_string.data(using: .utf8)!
actions = try JSONDecoder().decode([Event].self, from: actions_data)
}
}
and now I'm getting a similar error to before, but apparently on the Event
, not on the LogRow
anymore.
现在我收到了类似的错误,但显然是在事件上,而不是在LogRow上。
fatal error: Error raised at top level:
Swift.DecodingError.typeMismatch(Swift.String,
Swift.DecodingError.Context(codingPath: [Foundation.(_JSONKey in
_12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 1", intValue:
Optional(1)), tree_builder.Event.(CodingKeys in
_D5964B2C6A943A986EE24818C2C63D9B).symbol, tree_builder.Symbol.
(CodingKeys in _D5964B2C6A943A986EE24818C2C63D9B).properties,
Swift._DictionaryCodingKey(stringValue: "allowSubscript", intValue:
nil)], debugDescription: "Expected to decode String but found a number
instead.", underlyingError: nil)): file
/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-
900.0.65/src/swift/stdlib/public/core/ErrorType.swift, line 187
2 个解决方案
#1
1
You can use optional chaining, this is the solution given by Apple : https://developer.apple.com/swift/blog/?id=37
您可以使用可选链接,这是Apple提供的解决方案:https://developer.apple.com/swift/blog/?id = 37
There's also library that does it for you, like https://github.com/Hearst-DD/ObjectMapper, or https://github.com/tristanhimmelman/AlamofireObjectMapper
还有为你做的库,比如https://github.com/Hearst-DD/ObjectMapper,或https://github.com/tristanhimmelman/AlamofireObjectMapper
This is a basic example on how to do it, but you should refactor and take a look at the links I gave :
这是一个关于如何做的基本示例,但您应该重构并查看我给出的链接:
guard let event = jsonObject["event"] as? String, let timestamp = jsonObject["timestamp"] as? Int else {return}
myEvent.event = event
myEvent.timestamp = timestamp
if let symbol = jsonObject["symbol"] as? [String:Any] {
guard let symbolId = symbol["id"] as? Int, let type = symbol["type"] else {return}
let mySymbol = symbol()
mySymbol.id = id
mySymbol.type = type
}
EDIT
编辑
If you are using swift 4 and Codable protocol it should be really easy, you just have to make your nested objects as Codable too ! Then just :
如果您使用的是swift 4和Codable协议,它应该非常简单,您只需要将嵌套对象设置为Codable!然后就是:
try decoder.decode(yourType.self, from: yourJsonString)
If you use CodingKeys correctly it should do it.
如果你正确使用CodingKeys,它应该这样做。
#2
0
The problem was the declaration of properties
, as the JSON object does not encode a [String:String]
dictionary, since some of the values may not be String
s. So, this is the "answer" to this question, but that does not solve the nesting problem, so I created a new question: How do I parse a JSON object that has type-dependent sub-objects, in Swift?
问题是属性声明,因为JSON对象不编码[String:String]字典,因为某些值可能不是字符串。所以,这是这个问题的“答案”,但这并没有解决嵌套问题,所以我创建了一个新问题:如何在Swift中解析具有依赖于类型的子对象的JSON对象?
#1
1
You can use optional chaining, this is the solution given by Apple : https://developer.apple.com/swift/blog/?id=37
您可以使用可选链接,这是Apple提供的解决方案:https://developer.apple.com/swift/blog/?id = 37
There's also library that does it for you, like https://github.com/Hearst-DD/ObjectMapper, or https://github.com/tristanhimmelman/AlamofireObjectMapper
还有为你做的库,比如https://github.com/Hearst-DD/ObjectMapper,或https://github.com/tristanhimmelman/AlamofireObjectMapper
This is a basic example on how to do it, but you should refactor and take a look at the links I gave :
这是一个关于如何做的基本示例,但您应该重构并查看我给出的链接:
guard let event = jsonObject["event"] as? String, let timestamp = jsonObject["timestamp"] as? Int else {return}
myEvent.event = event
myEvent.timestamp = timestamp
if let symbol = jsonObject["symbol"] as? [String:Any] {
guard let symbolId = symbol["id"] as? Int, let type = symbol["type"] else {return}
let mySymbol = symbol()
mySymbol.id = id
mySymbol.type = type
}
EDIT
编辑
If you are using swift 4 and Codable protocol it should be really easy, you just have to make your nested objects as Codable too ! Then just :
如果您使用的是swift 4和Codable协议,它应该非常简单,您只需要将嵌套对象设置为Codable!然后就是:
try decoder.decode(yourType.self, from: yourJsonString)
If you use CodingKeys correctly it should do it.
如果你正确使用CodingKeys,它应该这样做。
#2
0
The problem was the declaration of properties
, as the JSON object does not encode a [String:String]
dictionary, since some of the values may not be String
s. So, this is the "answer" to this question, but that does not solve the nesting problem, so I created a new question: How do I parse a JSON object that has type-dependent sub-objects, in Swift?
问题是属性声明,因为JSON对象不编码[String:String]字典,因为某些值可能不是字符串。所以,这是这个问题的“答案”,但这并没有解决嵌套问题,所以我创建了一个新问题:如何在Swift中解析具有依赖于类型的子对象的JSON对象?