一些(null)喷json反序列化错误

时间:2015-05-28 22:50:16

标签: scala spray spray-json

我正在使用以下json对象获取http响应

{
transaction_hash: "fbb36255453bf8ff465d9ca5c427bd0e36cc799fda090cbcd62113f1f3e97cb4",
output_index: 0,
value: 2000000,
asset_id: null,
asset_quantity: null,
addresses: [
"1C4kYhyLftmkn48YarSoLupxHfYFo8kp64"
],
script_hex: "76a914795efb808598d6a24d1734b929fce1d4b713215188ac",
spent: false,
confirmations: 72935
}

这就是我将json对象读入本机scala对象的方法

 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")

  println("Asset Id: " + jsObject.fields.get("asset_id"))
  val assetId = jsObject.fields.get("asset_id") match {
    case Some(JsString(s)) => println("S : " + 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(e => BitcoinAddress(e.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])

最后这里是错误信息:

[info] - must create an unsigned nlocktime for all of the bitcoin in an address *** FAILED ***
[info]   spray.httpx.PipelineException: Some(null) (of class scala.Some)
[info]   at spray.httpx.ResponseTransformation$$anonfun$unmarshal$1.apply(ResponseTransformation.scala:36)
[info]   at spray.httpx.ResponseTransformation$$anonfun$unmarshal$1.apply(ResponseTransformation.scala:31)
[info]   at scala.util.Success$$anonfun$map$1.apply(Try.scala:236)
[info]   at scala.util.Try$.apply(Try.scala:191)
[info]   at scala.util.Success.map(Try.scala:236)
[info]   at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:235)
[info]   at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:235)
[info]   at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
[info]   at akka.dispatch.BatchingExecutor$Batch$$anonfun$run$1.processBatch$1(BatchingExecutor.scala:67)
[info]   at akka.dispatch.BatchingExecutor$Batch$$anonfun$run$1.apply$mcV$sp(BatchingExecutor.scala:82)
[info]   ...
[info]   Cause: scala.MatchError: Some(null) (of class scala.Some)
[info]   at com.coinprism.blockchain.UnspentTXOProtocol$UnspentTXOProtocolFormat$.read(UnspentTXOProtocol.scala:30)
[info]   at com.coinprism.blockchain.UnspentTXOProtocol$UnspentTXOProtocolFormat$.read(UnspentTXOProtocol.scala:21)
[info]   at spray.json.JsValue.convertTo(JsValue.scala:31)
[info]   at spray.json.CollectionFormats$$anon$1$$anonfun$read$1.apply(CollectionFormats.scala:28)
[info]   at spray.json.CollectionFormats$$anon$1$$anonfun$read$1.apply(CollectionFormats.scala:28)
[info]   at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
[info]   at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
[info]   at scala.collection.Iterator$class.foreach(Iterator.scala:743)
[info]   at scala.collection.AbstractIterator.foreach(Iterator.scala:1195)
[info]   at scala.collection.IterableLike$class.foreach(IterableLike.scala:72)

但是我收到一条错误消息,说我正在尝试将asset_id读入Some(null)。我想用spray json将null值反序列化为本机scala类型None?我在哪里错了?

1 个答案:

答案 0 :(得分:0)

为什么不使用DefaultJsonProtocol进行解析而是手动执行所有操作?甚至你的协议都有关于整数值的特殊格式的一些怪癖,spray-json可以为你做。

因此,您可以定义描述模型的案例类,并使用spray中的自动json映射器:

case class SomeReply (
                 transaction_hash:String,
                 output_index:Long,
                 value:Long,
                 asset_id:Option[Long],
                 asset_quantity:Option[Long],
                 addresses:List[String],
                 script_hex:String,
                 spent:Boolean,
                 confirmations:Long
                   )
object SomeReplyProtocol extends DefaultJsonProtocol {
  // custom format for (string|int) => number conversion
  implicit object StringNumFormat extends RootJsonFormat[Long] {
    val numberPattern = "([0-9]+)".r
    def write(value:Long) = JsNumber(value)
    def read(value:JsValue) = value match {
      case JsString(numberPattern(number)) => number.toLong
      case JsNumber(number) => number.toLong
      case _ => deserializationError("Cannot deserialize StringNumber")
    }
  }

  implicit val replyFmt = jsonFormat9(SomeReply)
}

所以你可以用这种方式使用这段代码:

object Main {
  val jsonString = """{
                 "transaction_hash": "fbb36255453bf8ff465d9ca5c427bd0e36cc799fda090cbcd62113f1f3e97cb4",
                 "output_index": 0,
                 "value": 2000000, 
                 "asset_id": null,
                 "asset_quantity": null,
                 "addresses": [
                 "1C4kYhyLftmkn48YarSoLupxHfYFo8kp64"
                 ],
                 "script_hex": "76a914795efb808598d6a24d1734b929fce1d4b713215188ac",
                 "spent": false,
                 "confirmations": 72935
                 }""" // value is a number
  val jsonStringQuirk = """{
                 "transaction_hash": "fbb36255453bf8ff465d9ca5c427bd0e36cc799fda090cbcd62113f1f3e97cb4",
                 "output_index": 0,
                 "value": "2000000", 
                 "asset_id": null,
                 "asset_quantity": null,
                 "addresses": [
                 "1C4kYhyLftmkn48YarSoLupxHfYFo8kp64"
                 ],
                 "script_hex": "76a914795efb808598d6a24d1734b929fce1d4b713215188ac",
                 "spent": false,
                 "confirmations": 72935
                 }""" // value is a String here!!
  def main(args: Array[String]): Unit = {
    import SomeReplyProtocol._
    val json = jsonString.parseJson.convertTo[SomeReply]
    val jsonQ = jsonStringQuirk.parseJson.convertTo[SomeReply]
    println(json) // null mapped to None, yay!
    println(jsonQ) // implicit string -> int conversion here
  }

}