— 1 min read
medium에 [iOS] Codable을 이용하여 JSON decoding하기 라는 포스팅을 작성한 적이 있었기 때문에 자신만만했지만 난관이 꽤 많았고 삽질이란 삽질은 다 했기 때문에 다시 정리하려고 한다.
위에서 언급한 포스팅에 작성한 것처럼 convertFromSnakeCase
를 사용했다.
1decoder.keyDecodingStrategy = .convertFromSnakeCase
API 문서에 써 있는 JSON 구조는 다음과 같았다.
1// Response - success2{3 "jsonrpc": "2.0",4 "id": 1234,5 "result": {6 "version": "0.1a",7 "prev_block_hash": "48757af881f76c858890fb41934bee228ad50a71707154a482826c39b8560d4b",8 "merkle_tree_root_hash": "fabc1884932cf52f657475b6d62adcbce5661754ff1a9d50f13f0c49c7d48c0c",9 "time_stamp": 1516498781094429,10 "confirmed_transaction_list": [ 11 {12 "version": "0x3",13 "from": "hxbe258ceb872e08851f1f59694dac2558708ece11",14 "to": "cxb0776ee37f5b45bfaea8cff1d8232fbb6122ec32",15 "value": "0xde0b6b3a7640000",16 "stepLimit": "0x12345",17 "timestamp": "0x563a6cf330136",18 "nid": "0x3",19 "nonce": "0x1",20 "signature": "VAia7YZ2Ji6igKWzjR2YsGa2m53nKPrfK7uXYW78QLE+ATehAVZPC40szvAiA6NEU5gCYB4c4qaQzqDh2ugcHgA=",21 "txHash": "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238",22 "dataType": "call",23 "data": {24 "method": "transfer",25 "params": {26 "to": "hxab2d8215eab14bc6bdd8bfb2c8151257032ecd8b",27 "value": "0x1"28 }29 }30 }31 ],32 "block_hash": "1fcf7c34dc875681761bdaa5d75d770e78e8166b5c4f06c226c53300cbe85f57",33 "height": 3,34 "peer_id": "hx86aba2210918a9b116973f3c4b27c41a54d5dafe",35 "signature": "MEQCICT8mTIL6pRwMWsJjSBHcl4QYiSgG8+0H3U32+05mO9HAiBOhIfBdHNm71WpAZYwJWwQbPVVXFJ8clXGKT3ScDWcvw=="36 }37}
그래서 이 문서에 맞춰서 아래와 같이 Decodable
모델을 구성했다.
1open class ConfirmedTransactionList: Decodable {2 public var from: String3 public var to: String4 public var timestamp: String5 public var signature: String6 public var txHash: String78 public var version: String?9 public var nid: String?10 public var stepLimit: String?11 public var value: String?1213 public var nonce: String?14 public var dataType: String?15 public var data: DataInfo?1617 public var fee: String?18 public var method: String?19 20 open class DataInfo: Decodable {21 public var method: String?22 public var params: [String: String]?23 }24}
잘 되는 줄 알고 이제 예제를 만들어보려고 하는데 Parsing error
가 떴다. :tired_face:
대체 왜…. 어제까지만 해도 잘 되던게… 안될까ㅠㅠㅠㅠ하고 그 문제가 일어나는 부분의 JSON을 보았더니 이런 식이었다.
1{2 "jsonrpc": "2.0",3 "result": {4 "version": "0.1a",5 "prev_block_hash": "3141e30db0b7036cfd750d9105d90ef95289c3a6f7e896999a4997c68c002fb8",6 "merkle_tree_root_hash": "d5a7106af08f96871c7c2bd6d5e16d5159486aff73f8d4ef84ff35e32d8d6b39",7 "time_stamp": 1550764885497421,8 "confirmed_transaction_list": [9 {10 "from": "hx04bebefa2c8c8378c20393d8259afe38a414e160",11 "to": "hx605b5ddd50a4b0314b93bcd80561aa5d37d4b46f",12 "value": "0x3635c9adc5dea00000",13 "version": "0x3",14 "nid": "0x1",15 "stepLimit": "0x193e8",16 "timestamp": "0x5826996223350",17 "data": "0x49434f4e65782074696c204c6564676572",18 "dataType": "message",19 "signature": "jxD3/Pe1OfIrgfsPgxReWxMbhSdTc8inp7mhoOps6ItS71vracx8UZdOrwgkoS7sFV1F8+ldN3LNQd2BZ9CykwE=",20 "txHash": "0xd5a7106af08f96871c7c2bd6d5e16d5159486aff73f8d4ef84ff35e32d8d6b39"21 }22 ],23 "block_hash": "bfbb0ae7770247bb2c7ca6d3b3fbcff398f4f3bc8bd68b7d511bc5e1d8499297",24 "height": 202131,25 "peer_id": "hx7563e2514a0865630216903c5fd166ec0fdb217a",26 "signature": "eSem6PHyHWxWyOretx0PAX+xEYyC+G0iuoS0ooIziy5iEzW4XrliQrHY59igC4d8krszw9TJoAYUAGsViZsLOwA="27 },28 "id": 123429}
data
라는 값이 두가지의 타입을 가져야하는 상황이었다.
그래서 갓구글에 검색해서 다음과 같은 답변을 찾아냈다.
https://stackoverflow.com/a/47319012
https://stackoverflow.com/a/50067514
enum을 이용하여 해결했다.
1open class ConfirmedTransactionList: Decodable {2 public var from: String3 public var to: String4 public var timestamp: String5 public var signature: String6 public var txHash: String78 public var version: String?9 public var nid: String?10 public var stepLimit: String?11 public var value: String?1213 public var nonce: String?14 public var dataType: String?15 16 public var data: DataValue?17 18 public var fee: String?19 public var method: String?20 21 public enum DataValue: Decodable {22 case string(String)23 case dataInfo(DataInfo)24 25 public init(from decoder: Decoder) throws {26 if let string = try? decoder.singleValueContainer().decode(String.self) {27 self = .string(string)28 return29 }3031 if let dataInfo = try? decoder.singleValueContainer().decode(DataInfo.self) {32 self = .dataInfo(dataInfo)33 return34 }35 throw DataValueError.missingValue36 }37 public enum DataValueError: Error {38 case missingValue39 }40 }41 42 open class DataInfo: Decodable {43 public var method: String?44 public var params: [String: String]?45 }46}
data에 있는 값 찾아오는 방법은 다음과 같다.
1let val = value.result.confirmedTransactionList.first?.data23switch val {4 case .string(let data)?:5 print("String: \(data)")6 case .dataInfo(let data)?:7 print("DataInfo: \(data.method) \(data.params)")8 case .none:9 print("None")10}