格式错误的 JSON 条目有时是一个数组有时是一个字典
Malformed JSON entry sometimes is an array sometimes a dictionary
我正在尝试解析 curl "https://blockchain.info/block-height/699999"
的结果
在其结果中,它 returns spending_outpoints
作为字典数组,但有时它使用空字典 "spending_outpoints" = { }
而不是数组 "spending_outpoints" = [ ... ]
。我已经实施了一个解决方法,但这也因以下错误而失败:
AppVisualizer/AppVisualizerApp.swift:71: Fatal error: 'try!'
expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"blocks", intValue: nil), _JSONKey(stringValue: "Index 0", intValue:
0), CodingKeys(stringValue: "tx", intValue: nil),
_JSONKey(stringValue: "Index 0", intValue: 0),
CodingKeys(stringValue: "inputs", intValue: nil),
_JSONKey(stringValue: "Index 0", intValue: 0),
CodingKeys(stringValue: "prev_out", intValue: nil),
CodingKeys(stringValue: "spending_outpoints", intValue: nil)],
debugDescription: "Expected to decode Dictionary<String, Any> but
found an array instead.", underlyingError: nil))
解决方法是枚举 SpendingOutpoints
,它可以是数组或字典。由于字典应为空,因此值类型应该无关紧要。
我的相关编码结构是:
struct SpendingOutpoint : Codable {
var tx_index : UInt64
var n : UInt32
}
enum SpendingOutpoints : Codable {
case array(Array<SpendingOutpoint>)
case dictionary(Dictionary<String,String>)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let array = try? container.decode(Array<SpendingOutpoint>.self, forKey: .array) {
self = .array(array)
return
}
if let dictionary = try? container.decode(Dictionary<String,String>.self, forKey: .dictionary) {
self = .dictionary(dictionary)
return
}
throw DecodingError.typeMismatch(SpendingOutpoints.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unexptected type: expecting array or dictionary"))
}
}
struct Output : Codable {
var type : Int
var spent : Bool
var script : String
var value : UInt64
var spending_outpoints : SpendingOutpoints
var tx_index : UInt64
var n : UInt64
var addr : String?
}
let url = Bundle.main.url(forResource: "blockheight", withExtension: "json")!
let data1 = try! JSONDecoder().decode(API.Blockchain.Blockheight.self, from: Data.init(contentsOf: url))
完整的Codable
结构集:
struct API {
struct Blockchain {
enum SpendingOutpoints : Codable {
case array(Array<SpendingOutpoint>)
case dictionary(Dictionary<String,String>)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let array = try? container.decode(Array<SpendingOutpoint>.self, forKey: .array) {
self = .array(array)
return
}
if let dictionary = try? container.decode(Dictionary<String,String>.self, forKey: .dictionary) {
self = .dictionary(dictionary)
return
}
throw DecodingError.typeMismatch(SpendingOutpoints.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unexptected type: expecting array or dictionary"))
}
}
struct SpendingOutpoint : Codable {
// {
// "tx_index":5602502841772452,
// "n":0
// }
var tx_index : UInt64
var n : UInt32
}
struct Output : Codable {
// "type":0,
// "spent":true,
// "value":626178339,
// "spending_outpoints":[
// {
// "tx_index":4405887896017968,
// "n":2
// }
// ],
// "n":0,
// "tx_index":4989293645553533,
// "script":"76a91474e878616bd5e5236ecb22667627eeecbff54b9f88ac",
// "addr":"1Bf9sZvBHPFGVPX71WX2njhd1NXKv5y7v5"
var type : Int
var spent : Bool
var script : String
var value : UInt64
var spending_outpoints : SpendingOutpoints
var tx_index : UInt64
var n : UInt64
var addr : String?
}
struct Input : Codable {
// "sequence":4294967295,
// "witness":"01200000000000000000000000000000000000000000000000000000000000000000",
// "script":"03c9f00a1362696e616e63652f3831305000fb0162c56408fabe6d6d85e315917dcf79bdee12fcf0b412ca00bdbe3ec986dec2a30884d4b5ff512d1a0200000000000000a4aa0000ec190200",
// "index":0,
// "prev_out":{
var sequence : UInt64
var witness : String
var script : String
var index : UInt32
var prev_out : Output
}
struct Tx : Codable {
// "hash":"f2b8c6f1586e238d9f56e16fdfe8d31e5c086be5889cb7dbcf244de5bd923b9f",
// "ver":1,
// "vin_sz":1,
// "vout_sz":4,
// "size":343,
// "weight":1264,
// "fee":0,
// "relayed_by":"0.0.0.0",
// "lock_time":0,
// "tx_index":5602502841772452,
// "double_spend":false,
// "time":1641209474,
// "block_index":717001,
// "block_height":717001,
// "inputs":[
var hash : String
var ver : Int
var vin_sz : Int
var vout_sz : Int
var size : Int
var weight : Int
var fee : Double
var relayed_by : String
var lock_time : UInt32
var tx_index : UInt64
var double_spend : Bool
var time : UInt32
var block_index : UInt32
var block_height : UInt32
var inputs : [Input]
var out : [Output]
}
struct Block : Codable {
// "hash":"00000000000000000003d03aab20439e69f319e847f101ccd8a7cfbef9473561",
// "ver":545259520,
// "prev_block":"00000000000000000009396fbe5531a11f90bae421aa1621def645e0fe5e4e1b",
// "mrkl_root":"f2b8c6f1586e238d9f56e16fdfe8d31e5c086be5889cb7dbcf244de5bd923b9f",
// "time":1641209474,
// "bits":386635947,
// "next_block":[
// "0000000000000000000b6713adeb66d64cacefbcc5b402db608f75d6edd0f2cf"
// ],
// "fee":0,
// "nonce":3212120200,
// "n_tx":1,
// "size":424,
// "block_index":717001,
// "main_chain":true,
// "height":717001,
// "weight":1588,
// "tx":[
var hash : String
var ver : UInt64
var prev_block : String
var mrkl_root : String
var time : UInt64
var bits : UInt64
var next_block : [String]
var fee : UInt64
var nonce : UInt32
var n_tx : Int
var size : Int
var block_index : UInt
var main_chain : Bool
var height : UInt32
var weight : UInt32
var tx : [Tx]
}
struct Blockheight : Codable {
var blocks : [Block]
}
}
}
这是 json 文件:
{
"blocks":[
{
"hash":"0000000000000000000aa3ce000eb559f4143be419108134e0ce71042fc636eb",
"ver":832872452,
"prev_block":"00000000000000000007a60f43d15d097bd369dd8c6c53f12b32f649f979e85e",
"mrkl_root":"a961ea5c3420ca74d28e33e7dfe5e871da6db36ee63d62687e791a9f3098596d",
"time":1631332753,
"bits":386877668,
"next_block":[
"0000000000000000000590fc0f3eba193a278534220b2b37e9849e1a770ca959"
],
"fee":1178339,
"nonce":648207636,
"n_tx":292,
"size":141595,
"block_index":699999,
"main_chain":true,
"height":699999,
"weight":372724,
"tx":[
{
"hash":"3a7e5833d940a75fc542364d4eba8e3ea37b84f4c061b35c3ceafb629be3cd8d",
"ver":2,
"vin_sz":1,
"vout_sz":3,
"size":293,
"weight":1064,
"fee":0,
"relayed_by":"0.0.0.0",
"lock_time":0,
"tx_index":4989293645553533,
"double_spend":false,
"time":1631332753,
"block_index":699999,
"block_height":699999,
"inputs":[
{
"sequence":4294967295,
"witness":"01200000000000000000000000000000000000000000000000000000000000000000",
"script":"035fae0a0413789b644254432e636f6d2f7563386575fabe6d6da8a2adde9c18f88da1fd1c3c529ce4165f9e4b2f87164bcb9f1a7a07b0c89dce020000008e9b20aa2058499a0000000000000000",
"index":0,
"prev_out":{
"spent":true,
"script":"",
"spending_outpoints":[
{
"tx_index":4989293645553533,
"n":0
}
],
"tx_index":0,
"value":0,
"n":4294967295,
"type":0
}
}
],
"out":[
{
"type":0,
"spent":true,
"value":626178339,
"spending_outpoints":[
{
"tx_index":4405887896017968,
"n":2
}
],
"n":0,
"tx_index":4989293645553533,
"script":"76a91474e878616bd5e5236ecb22667627eeecbff54b9f88ac",
"addr":"1Bf9sZvBHPFGVPX71WX2njhd1NXKv5y7v5"
},
{
"type":0,
"spent":false,
"value":0,
"spending_outpoints":{
},
"n":1,
"tx_index":4989293645553533,
"script":"6a24aa21a9ed2f5cb87274312f2e7f785e34be6db44afec9d1263eb0977b6e3c8c7f7df6ec7d"
},
{
"type":0,
"spent":false,
"value":0,
"spending_outpoints":{
},
"n":2,
"tx_index":4989293645553533,
"script":"6a24b9e11b6de2202207ad664f985557d8de23adafd23c36fc708614dd2220b58e735dfd0ca9"
}
]
}
]
}
]
}
这是一个可行的解决方案。为了演示,从样本 JSON 中删除所有数据就足够了,spending_outpoints
和周围的数据结构除外。
let json = """
[
{
"spending_outpoints": [{
"tx_index": 4405887896017968,
"n": 2
}]
},
{
"spending_outpoints": {}
},
{
"spending_outpoints": {}
}
]
"""
这是我们获取字典数组时的字典结构:
struct Outpoint: Decodable {
let tx_index: Int
let n: Int
}
这是接受数组或(空)字典的联合枚举:
enum ArrayOrDict: Decodable {
case array(Array<Outpoint>)
case dict(Dictionary<String,String>)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let array = try? container.decode(Array<Outpoint>.self) {
self = .array(array)
return
}
if let dict = try? container.decode(Dictionary<String,String>.self) {
self = .dict(dict)
return
}
throw DecodingError.typeMismatch(ArrayOrDict.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Default"))
}
}
整体字典类型如下:
struct Outer: Decodable {
let spendingOutpoints: ArrayOrDict
enum CodingKeys: String, CodingKey {
case spendingOutpoints = "spending_outpoints"
}
}
好的,我们开始:
let jsonData = json.data(using: .utf8)!
do {
let result = try JSONDecoder().decode([Outer].self, from: jsonData)
} catch {
print(error)
}
没有错误。因此,您应该能够重新构建您的实际数据结构。
我正在尝试解析 curl "https://blockchain.info/block-height/699999"
在其结果中,它 returns spending_outpoints
作为字典数组,但有时它使用空字典 "spending_outpoints" = { }
而不是数组 "spending_outpoints" = [ ... ]
。我已经实施了一个解决方法,但这也因以下错误而失败:
AppVisualizer/AppVisualizerApp.swift:71: Fatal error: 'try!'
expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.Dictionary<Swift.String, Any>,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"blocks", intValue: nil), _JSONKey(stringValue: "Index 0", intValue:
0), CodingKeys(stringValue: "tx", intValue: nil),
_JSONKey(stringValue: "Index 0", intValue: 0),
CodingKeys(stringValue: "inputs", intValue: nil),
_JSONKey(stringValue: "Index 0", intValue: 0),
CodingKeys(stringValue: "prev_out", intValue: nil),
CodingKeys(stringValue: "spending_outpoints", intValue: nil)],
debugDescription: "Expected to decode Dictionary<String, Any> but
found an array instead.", underlyingError: nil))
解决方法是枚举 SpendingOutpoints
,它可以是数组或字典。由于字典应为空,因此值类型应该无关紧要。
我的相关编码结构是:
struct SpendingOutpoint : Codable {
var tx_index : UInt64
var n : UInt32
}
enum SpendingOutpoints : Codable {
case array(Array<SpendingOutpoint>)
case dictionary(Dictionary<String,String>)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let array = try? container.decode(Array<SpendingOutpoint>.self, forKey: .array) {
self = .array(array)
return
}
if let dictionary = try? container.decode(Dictionary<String,String>.self, forKey: .dictionary) {
self = .dictionary(dictionary)
return
}
throw DecodingError.typeMismatch(SpendingOutpoints.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unexptected type: expecting array or dictionary"))
}
}
struct Output : Codable {
var type : Int
var spent : Bool
var script : String
var value : UInt64
var spending_outpoints : SpendingOutpoints
var tx_index : UInt64
var n : UInt64
var addr : String?
}
let url = Bundle.main.url(forResource: "blockheight", withExtension: "json")!
let data1 = try! JSONDecoder().decode(API.Blockchain.Blockheight.self, from: Data.init(contentsOf: url))
完整的Codable
结构集:
struct API {
struct Blockchain {
enum SpendingOutpoints : Codable {
case array(Array<SpendingOutpoint>)
case dictionary(Dictionary<String,String>)
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let array = try? container.decode(Array<SpendingOutpoint>.self, forKey: .array) {
self = .array(array)
return
}
if let dictionary = try? container.decode(Dictionary<String,String>.self, forKey: .dictionary) {
self = .dictionary(dictionary)
return
}
throw DecodingError.typeMismatch(SpendingOutpoints.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unexptected type: expecting array or dictionary"))
}
}
struct SpendingOutpoint : Codable {
// {
// "tx_index":5602502841772452,
// "n":0
// }
var tx_index : UInt64
var n : UInt32
}
struct Output : Codable {
// "type":0,
// "spent":true,
// "value":626178339,
// "spending_outpoints":[
// {
// "tx_index":4405887896017968,
// "n":2
// }
// ],
// "n":0,
// "tx_index":4989293645553533,
// "script":"76a91474e878616bd5e5236ecb22667627eeecbff54b9f88ac",
// "addr":"1Bf9sZvBHPFGVPX71WX2njhd1NXKv5y7v5"
var type : Int
var spent : Bool
var script : String
var value : UInt64
var spending_outpoints : SpendingOutpoints
var tx_index : UInt64
var n : UInt64
var addr : String?
}
struct Input : Codable {
// "sequence":4294967295,
// "witness":"01200000000000000000000000000000000000000000000000000000000000000000",
// "script":"03c9f00a1362696e616e63652f3831305000fb0162c56408fabe6d6d85e315917dcf79bdee12fcf0b412ca00bdbe3ec986dec2a30884d4b5ff512d1a0200000000000000a4aa0000ec190200",
// "index":0,
// "prev_out":{
var sequence : UInt64
var witness : String
var script : String
var index : UInt32
var prev_out : Output
}
struct Tx : Codable {
// "hash":"f2b8c6f1586e238d9f56e16fdfe8d31e5c086be5889cb7dbcf244de5bd923b9f",
// "ver":1,
// "vin_sz":1,
// "vout_sz":4,
// "size":343,
// "weight":1264,
// "fee":0,
// "relayed_by":"0.0.0.0",
// "lock_time":0,
// "tx_index":5602502841772452,
// "double_spend":false,
// "time":1641209474,
// "block_index":717001,
// "block_height":717001,
// "inputs":[
var hash : String
var ver : Int
var vin_sz : Int
var vout_sz : Int
var size : Int
var weight : Int
var fee : Double
var relayed_by : String
var lock_time : UInt32
var tx_index : UInt64
var double_spend : Bool
var time : UInt32
var block_index : UInt32
var block_height : UInt32
var inputs : [Input]
var out : [Output]
}
struct Block : Codable {
// "hash":"00000000000000000003d03aab20439e69f319e847f101ccd8a7cfbef9473561",
// "ver":545259520,
// "prev_block":"00000000000000000009396fbe5531a11f90bae421aa1621def645e0fe5e4e1b",
// "mrkl_root":"f2b8c6f1586e238d9f56e16fdfe8d31e5c086be5889cb7dbcf244de5bd923b9f",
// "time":1641209474,
// "bits":386635947,
// "next_block":[
// "0000000000000000000b6713adeb66d64cacefbcc5b402db608f75d6edd0f2cf"
// ],
// "fee":0,
// "nonce":3212120200,
// "n_tx":1,
// "size":424,
// "block_index":717001,
// "main_chain":true,
// "height":717001,
// "weight":1588,
// "tx":[
var hash : String
var ver : UInt64
var prev_block : String
var mrkl_root : String
var time : UInt64
var bits : UInt64
var next_block : [String]
var fee : UInt64
var nonce : UInt32
var n_tx : Int
var size : Int
var block_index : UInt
var main_chain : Bool
var height : UInt32
var weight : UInt32
var tx : [Tx]
}
struct Blockheight : Codable {
var blocks : [Block]
}
}
}
这是 json 文件:
{
"blocks":[
{
"hash":"0000000000000000000aa3ce000eb559f4143be419108134e0ce71042fc636eb",
"ver":832872452,
"prev_block":"00000000000000000007a60f43d15d097bd369dd8c6c53f12b32f649f979e85e",
"mrkl_root":"a961ea5c3420ca74d28e33e7dfe5e871da6db36ee63d62687e791a9f3098596d",
"time":1631332753,
"bits":386877668,
"next_block":[
"0000000000000000000590fc0f3eba193a278534220b2b37e9849e1a770ca959"
],
"fee":1178339,
"nonce":648207636,
"n_tx":292,
"size":141595,
"block_index":699999,
"main_chain":true,
"height":699999,
"weight":372724,
"tx":[
{
"hash":"3a7e5833d940a75fc542364d4eba8e3ea37b84f4c061b35c3ceafb629be3cd8d",
"ver":2,
"vin_sz":1,
"vout_sz":3,
"size":293,
"weight":1064,
"fee":0,
"relayed_by":"0.0.0.0",
"lock_time":0,
"tx_index":4989293645553533,
"double_spend":false,
"time":1631332753,
"block_index":699999,
"block_height":699999,
"inputs":[
{
"sequence":4294967295,
"witness":"01200000000000000000000000000000000000000000000000000000000000000000",
"script":"035fae0a0413789b644254432e636f6d2f7563386575fabe6d6da8a2adde9c18f88da1fd1c3c529ce4165f9e4b2f87164bcb9f1a7a07b0c89dce020000008e9b20aa2058499a0000000000000000",
"index":0,
"prev_out":{
"spent":true,
"script":"",
"spending_outpoints":[
{
"tx_index":4989293645553533,
"n":0
}
],
"tx_index":0,
"value":0,
"n":4294967295,
"type":0
}
}
],
"out":[
{
"type":0,
"spent":true,
"value":626178339,
"spending_outpoints":[
{
"tx_index":4405887896017968,
"n":2
}
],
"n":0,
"tx_index":4989293645553533,
"script":"76a91474e878616bd5e5236ecb22667627eeecbff54b9f88ac",
"addr":"1Bf9sZvBHPFGVPX71WX2njhd1NXKv5y7v5"
},
{
"type":0,
"spent":false,
"value":0,
"spending_outpoints":{
},
"n":1,
"tx_index":4989293645553533,
"script":"6a24aa21a9ed2f5cb87274312f2e7f785e34be6db44afec9d1263eb0977b6e3c8c7f7df6ec7d"
},
{
"type":0,
"spent":false,
"value":0,
"spending_outpoints":{
},
"n":2,
"tx_index":4989293645553533,
"script":"6a24b9e11b6de2202207ad664f985557d8de23adafd23c36fc708614dd2220b58e735dfd0ca9"
}
]
}
]
}
]
}
这是一个可行的解决方案。为了演示,从样本 JSON 中删除所有数据就足够了,spending_outpoints
和周围的数据结构除外。
let json = """
[
{
"spending_outpoints": [{
"tx_index": 4405887896017968,
"n": 2
}]
},
{
"spending_outpoints": {}
},
{
"spending_outpoints": {}
}
]
"""
这是我们获取字典数组时的字典结构:
struct Outpoint: Decodable {
let tx_index: Int
let n: Int
}
这是接受数组或(空)字典的联合枚举:
enum ArrayOrDict: Decodable {
case array(Array<Outpoint>)
case dict(Dictionary<String,String>)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let array = try? container.decode(Array<Outpoint>.self) {
self = .array(array)
return
}
if let dict = try? container.decode(Dictionary<String,String>.self) {
self = .dict(dict)
return
}
throw DecodingError.typeMismatch(ArrayOrDict.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Default"))
}
}
整体字典类型如下:
struct Outer: Decodable {
let spendingOutpoints: ArrayOrDict
enum CodingKeys: String, CodingKey {
case spendingOutpoints = "spending_outpoints"
}
}
好的,我们开始:
let jsonData = json.data(using: .utf8)!
do {
let result = try JSONDecoder().decode([Outer].self, from: jsonData)
} catch {
print(error)
}
没有错误。因此,您应该能够重新构建您的实际数据结构。