0

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
  }
]

そして、ここに私がそれを解析しようとしているケースクラスがあります:

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 Requestを解析する方法です:

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 配列を返していることにあるのではないかと思いgetUnspentTXOsます。エラーメッセージは非常に曖昧なようです。スプレーは、json フィールドをケース クラスVector内ではなく、内にラップしようとしているようです。UnspentTXOなぜこれが起こっているのかはわかりません。

4

1 に答える 1

2

convertTo[ Option[ Long ] ]とだけを呼び出すことはできませんconvertTo[ List [ BitcointAddress ] ]

はこのようconvertToに定義されており、

def convertTo[T :JsonReader]: T = jsonReader[T].read(this)

つまり...Timplicit証拠typeclass JsonReader[ T ]が利用可能なタイプのみを で使用できますconvertTo

implicit typeclass適切な証拠を提供しない限り、いくつかのケースを特別に処理する必要があります。

これ以外は、spray-jsonあまりにもミニマルです...そのためJsObject、上のラッパーでありMap[ String, JsValue]、次のgetFieldsように定義されています。

 def getFields(fieldNames: String*): immutable.Seq[JsValue] =
   fieldNames.flatMap(fields.get)(collection.breakOut)

つまり...マップに存在しない要求されたフィールドを無視し、存在する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 ] ]取得して作業する別の方法は、最も一般的に使用されるタイプの json 形式を提供convertTo[ List[ T ] ]するインポートです。しかし、その場合でも、スコープ内に証拠がspray.json.DefaultJsonProtocol._なければなりません。implicittypeclass JsonReader[ T ]

于 2015-02-27T13:44:39.133 に答える