2017-09-26 59 views
0

我有到位如何解析嵌套的JSON字符串作爲Swift中的對象?

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 
} 

和以下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 
    } 
] 

的JSON數據來自一個數據庫,通過DataGrip的JSON出口導出的以下數據結構。數據庫字段的類型是JSON Blob(它是Postgres,所以沒關係)。查詢是這樣的:

SELECT 
    ... 
    event_details->>'actions' AS actions, 
    ... 
FROM yadda_yadda...; 

萬一它是有用的(是的,我也試過單箭頭版本,沒有區別)。

我的問題是我該如何讓Swift將actions字符串解析爲[Event]作爲JSON對象?或者,有沒有辦法使DataGrip(或Postgres)將該字段導出爲對象而不是序列化的對象?

編輯我改變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) 
    } 
} 

,現在我得到一個類似的錯誤之前,但顯然對Event,而不是在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 
+0

首先,你應該擺脫所有這些隱式解包的optionals。這個答案告訴你如何完成它https://stackoverflow.com/a/43121890/2303865 –

+0

我通常會,但在這種情況下,我絕對肯定我可以隱式解開我的選擇權,因爲我完全控制環境,包括數據集。如果我要在野外釋放它,我肯定會擺脫這一點。 – Morpheu5

+2

這是不是,如果你可以或不可以。你不需要它。 –

回答

1

您可以使用可選的鏈接,這是由蘋果公司提供的解決方案:https://developer.apple.com/swift/blog/?id=37

還有庫,它會爲你,就像https://github.com/Hearst-DD/ObjectMapper,或https://github.com/tristanhimmelman/AlamofireObjectMapper

這是一個基本的例子如何做到這一點,但你應該重構,並看看我給的鏈接:

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 
} 

編輯

如果您正在使用SWIFT 4和可編碼協議,它應該是很容易的,你就必須讓你的嵌套對象爲可編碼的呢!然後只是:

try decoder.decode(yourType.self, from: yourJsonString) 

如果您正確使用CodingKeys它應該這樣做。

+0

Uuuh,我的問題不是如何將json字符串解碼爲對象。如果我將一個測試字符串提供給解碼器並要求它吐出一個'Event'對象,那麼這會起作用。我的問題是:假設序列化的LogRow包含一個字符串,該字符串包含在其「actions」鍵中的一個「Event」數組的序列化表示,如何使解碼器對該字符串進行反序列化並將其轉換爲「 Event's? – Morpheu5

+0

https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types –