Cryptic Spray Json 错误消息
Cryptic Spray Json error message
我在尝试解析某些 json:
时收到以下错误消息
[info] The future returned an exception of type: spray.httpx.PipelineException, with message:
Vector("eba760a81b177051b0520418b4e10596955adb98196c15367a2467ab66a19b5c", 600, "AN51SPP6iZBHFJ3aux1jtn6MMMD13Gh3t7", 500,
["1BXVXP82f7x9YWdWuCaCYwad8ZoYayyRYt"], "76a91473758c13a91699376abb8fe76931bdd9bdc04ee388ac", false)
(of class scala.collection.immutable.Vector). (AddressUnspentTXORequestTest.scala:14)
我不太确定那是什么意思。这是我要解析的 json 的一部分:
[
{
"transaction_hash": "eba760a81b177051b0520418b4e10596955adb98196c15367a2467ab66a19b5c",
"output_index": 1,
"value": 600,
"asset_id": "AN51SPP6iZBHFJ3aux1jtn6MMMD13Gh3t7",
"asset_quantity": 500,
"addresses": [
"1BXVXP82f7x9YWdWuCaCYwad8ZoYayyRYt"
],
"script_hex": "76a91473758c13a91699376abb8fe76931bdd9bdc04ee388ac",
"spent": false,
"confirmations": 31674
},
{
"transaction_hash": "1f9f6224bee8813135aba622693c78a33b3460e4efdb340174f87fdd8c9d4148",
"output_index": 1,
"value": 600,
"asset_id": "AS6tDJJ3oWrcE1Kk3T14mD8q6ycHYVzyYQ",
"asset_quantity": 200000,
"addresses": [
"1BXVXP82f7x9YWdWuCaCYwad8ZoYayyRYt"
],
"script_hex": "76a91473758c13a91699376abb8fe76931bdd9bdc04ee388ac",
"spent": false,
"confirmations": 35895
}
]
这是我试图将其解析为 class 的情况:
case class UnspentTXO(transaction_hash: String, output_index: Int, value: Long,
asset_id: Option[String], asset_quantity: Option[Long], addresses: List[BitcoinAddress],
script_hex: String, spent: Boolean)
发起请求的方法在这里:
def getUnspentTXOs(address: Address): Future[List[UnspentTXO]] = {
val pipeline: HttpRequest => Future[List[UnspentTXO]] =
sendReceive ~> unmarshal[List[UnspentTXO]]
pipeline(Get(host + path + address.value + "/unspents"))
}
最后这就是我解析 Json 请求的方式:
override def read(value: JsValue): UnspentTXO = {
val Seq(transaction_hash, output_index, locked_satoshies, asset_id, asset_quantity, addresses, script_hex, spent) =
value.asJsObject.getFields("transaction_hash", "value", "asset_id", "asset_quantity", "addresses", "script_hex", "spent")
val assetId = asset_id match {
case JsString(s) => Some(s)
case JsNull => None
case _ => throw new RuntimeException("Asset id should be of type JsString or JsNull, got something else")
}
val assetQuantity = asset_quantity match {
case JsNumber(n) => Some(n.toLong)
case JsNull => None
case _ => throw new RuntimeException("Asset quantity should be JsNull or a JsNumber")
}
// convert JsArray to List[ BitcoinAdress ]
val addressList = addresses match {
case ja: JsArray => {
ja.elements.toList.map( e => BitcoinAddress(e.convertTo[String]))
}
case _ => throw new RuntimeException("address list should be of type JsArray, got something else")
}
UnspentTXO(transaction_hash.convertTo[String], output_index.convertTo[Int], locked_satoshies.convertTo[Long],
assetId, assetQuantity, addressList,
script_hex.convertTo[String], spent.convertTo[Boolean])
}
我认为问题可能是请求返回了一个 json 数组,而不仅仅是一个 JSON 对象,所以我不确定我是否在我的 [=] 中正确处理了它16=]。错误信息似乎很含糊。 spray 似乎试图将 json 字段包装在 Vector
内部,而不是 UnspentTXO
案例 class 内部。我不确定为什么会这样。
你不能只调用 convertTo[ Option[ Long ] ]
和 convertTo[ List [ BitcointAddress ] ]
。
convertTo
是这样定义的,
def convertTo[T :JsonReader]: T = jsonReader[T].read(this)
这意味着...只有implicit
typeclass
JsonReader[ T ]
证据可用的类型 T
才能与 convertTo
一起使用。 =32=]
除非您提供适当的implicit typeclass
证据,否则您将不得不专门处理少数情况。
除此之外,spray-json
太简约了...所以 JsObject
只是 Map[ String, JsValue]
之上的包装,getFields
定义为,
def getFields(fieldNames: String*): immutable.Seq[JsValue] =
fieldNames.flatMap(fields.get)(collection.breakOut)
这意味着...它只会忽略映射中不存在的询问字段,并且只有 return 与存在的询问字段相对应的 JsValue
序列。
因此必须检查可选值是否存在于地图中。或者我们将不得不为每种可能的情况进行模式匹配(这可能导致很多情况)。
override def read(value: JsValue): UnspentTXO = {
val jsObject = value.asJsObject
// get only non-optional values here
val Seq( transaction_hash, output_index, locked_satoshies, addresses,
script_hex, spent ) =
jsObject.getFields( "transaction_hash", "output_index", "value", "addresses", "script_hex", "spent" )
// Assuming you have imported spray.json._, simple types will work.
// have to handle options differently
// or 2 optional values would mean 4 patterns-matchings of sequences like above.
// jsObject.fields is just a Map[ String, JsValue ]
val assetId = jsObject.fields.get( "asset_id" ) match {
case Some( JsString( s ) ) => Some( s )
case None => None
}
val assetQuantity = jsObject.fields.get( "asset_quantity" ) match {
case Some( JsNumber( n ) ) => Some( n.toLong )
case None => None
}
// convert JsArray to List[ BitcoinAdress ]
val addressList = addresses match {
case ja : JsArray => {
ja.elements.toList.map( BitcoinAddress( _.convertTo[ String ] ) )
}
}
UnspentTXO( transaction_hash.convertTo[ String ], output_index.convertTo[ Int ],
locked_satoshies.convertTo[ Long ], assetId, assetQuantity,
addressList, script_hex.convertTo[ String ], spent.convertTo[ Boolean ] )
}
另一种使 convertTo[ Option[ T ] ]
和 convertTo[ List[ T ] ]
工作的方法是导入 spray.json.DefaultJsonProtocol._
,它为大多数常用类型提供 json 格式。但即便如此,您的范围内也必须有 implicit
个 typeclass JsonReader[ T ]
的证据。
我在尝试解析某些 json:
时收到以下错误消息[info] The future returned an exception of type: spray.httpx.PipelineException, with message:
Vector("eba760a81b177051b0520418b4e10596955adb98196c15367a2467ab66a19b5c", 600, "AN51SPP6iZBHFJ3aux1jtn6MMMD13Gh3t7", 500,
["1BXVXP82f7x9YWdWuCaCYwad8ZoYayyRYt"], "76a91473758c13a91699376abb8fe76931bdd9bdc04ee388ac", false)
(of class scala.collection.immutable.Vector). (AddressUnspentTXORequestTest.scala:14)
我不太确定那是什么意思。这是我要解析的 json 的一部分:
[
{
"transaction_hash": "eba760a81b177051b0520418b4e10596955adb98196c15367a2467ab66a19b5c",
"output_index": 1,
"value": 600,
"asset_id": "AN51SPP6iZBHFJ3aux1jtn6MMMD13Gh3t7",
"asset_quantity": 500,
"addresses": [
"1BXVXP82f7x9YWdWuCaCYwad8ZoYayyRYt"
],
"script_hex": "76a91473758c13a91699376abb8fe76931bdd9bdc04ee388ac",
"spent": false,
"confirmations": 31674
},
{
"transaction_hash": "1f9f6224bee8813135aba622693c78a33b3460e4efdb340174f87fdd8c9d4148",
"output_index": 1,
"value": 600,
"asset_id": "AS6tDJJ3oWrcE1Kk3T14mD8q6ycHYVzyYQ",
"asset_quantity": 200000,
"addresses": [
"1BXVXP82f7x9YWdWuCaCYwad8ZoYayyRYt"
],
"script_hex": "76a91473758c13a91699376abb8fe76931bdd9bdc04ee388ac",
"spent": false,
"confirmations": 35895
}
]
这是我试图将其解析为 class 的情况:
case class UnspentTXO(transaction_hash: String, output_index: Int, value: Long,
asset_id: Option[String], asset_quantity: Option[Long], addresses: List[BitcoinAddress],
script_hex: String, spent: Boolean)
发起请求的方法在这里:
def getUnspentTXOs(address: Address): Future[List[UnspentTXO]] = {
val pipeline: HttpRequest => Future[List[UnspentTXO]] =
sendReceive ~> unmarshal[List[UnspentTXO]]
pipeline(Get(host + path + address.value + "/unspents"))
}
最后这就是我解析 Json 请求的方式:
override def read(value: JsValue): UnspentTXO = {
val Seq(transaction_hash, output_index, locked_satoshies, asset_id, asset_quantity, addresses, script_hex, spent) =
value.asJsObject.getFields("transaction_hash", "value", "asset_id", "asset_quantity", "addresses", "script_hex", "spent")
val assetId = asset_id match {
case JsString(s) => Some(s)
case JsNull => None
case _ => throw new RuntimeException("Asset id should be of type JsString or JsNull, got something else")
}
val assetQuantity = asset_quantity match {
case JsNumber(n) => Some(n.toLong)
case JsNull => None
case _ => throw new RuntimeException("Asset quantity should be JsNull or a JsNumber")
}
// convert JsArray to List[ BitcoinAdress ]
val addressList = addresses match {
case ja: JsArray => {
ja.elements.toList.map( e => BitcoinAddress(e.convertTo[String]))
}
case _ => throw new RuntimeException("address list should be of type JsArray, got something else")
}
UnspentTXO(transaction_hash.convertTo[String], output_index.convertTo[Int], locked_satoshies.convertTo[Long],
assetId, assetQuantity, addressList,
script_hex.convertTo[String], spent.convertTo[Boolean])
}
我认为问题可能是请求返回了一个 json 数组,而不仅仅是一个 JSON 对象,所以我不确定我是否在我的 [=] 中正确处理了它16=]。错误信息似乎很含糊。 spray 似乎试图将 json 字段包装在 Vector
内部,而不是 UnspentTXO
案例 class 内部。我不确定为什么会这样。
你不能只调用 convertTo[ Option[ Long ] ]
和 convertTo[ List [ BitcointAddress ] ]
。
convertTo
是这样定义的,
def convertTo[T :JsonReader]: T = jsonReader[T].read(this)
这意味着...只有implicit
typeclass
JsonReader[ T ]
证据可用的类型 T
才能与 convertTo
一起使用。 =32=]
除非您提供适当的implicit typeclass
证据,否则您将不得不专门处理少数情况。
除此之外,spray-json
太简约了...所以 JsObject
只是 Map[ String, JsValue]
之上的包装,getFields
定义为,
def getFields(fieldNames: String*): immutable.Seq[JsValue] =
fieldNames.flatMap(fields.get)(collection.breakOut)
这意味着...它只会忽略映射中不存在的询问字段,并且只有 return 与存在的询问字段相对应的 JsValue
序列。
因此必须检查可选值是否存在于地图中。或者我们将不得不为每种可能的情况进行模式匹配(这可能导致很多情况)。
override def read(value: JsValue): UnspentTXO = {
val jsObject = value.asJsObject
// get only non-optional values here
val Seq( transaction_hash, output_index, locked_satoshies, addresses,
script_hex, spent ) =
jsObject.getFields( "transaction_hash", "output_index", "value", "addresses", "script_hex", "spent" )
// Assuming you have imported spray.json._, simple types will work.
// have to handle options differently
// or 2 optional values would mean 4 patterns-matchings of sequences like above.
// jsObject.fields is just a Map[ String, JsValue ]
val assetId = jsObject.fields.get( "asset_id" ) match {
case Some( JsString( s ) ) => Some( s )
case None => None
}
val assetQuantity = jsObject.fields.get( "asset_quantity" ) match {
case Some( JsNumber( n ) ) => Some( n.toLong )
case None => None
}
// convert JsArray to List[ BitcoinAdress ]
val addressList = addresses match {
case ja : JsArray => {
ja.elements.toList.map( BitcoinAddress( _.convertTo[ String ] ) )
}
}
UnspentTXO( transaction_hash.convertTo[ String ], output_index.convertTo[ Int ],
locked_satoshies.convertTo[ Long ], assetId, assetQuantity,
addressList, script_hex.convertTo[ String ], spent.convertTo[ Boolean ] )
}
另一种使 convertTo[ Option[ T ] ]
和 convertTo[ List[ T ] ]
工作的方法是导入 spray.json.DefaultJsonProtocol._
,它为大多数常用类型提供 json 格式。但即便如此,您的范围内也必须有 implicit
个 typeclass JsonReader[ T ]
的证据。