Json error parsing into case class, json array

186 views Asked by At

I am trying to parse this json object and I ran a unit test and I got this error Error: Error when parsing result of 'listdescriptors': {"obj":[{"msg":["error.expected.jsarray"],"args":[]}]}! [info] at org.bitcoins.rpc.client.common.Client.parseResult(Client.scala:455). I think the problem may be that I'm parsing the json file into a case class incorectly.

Result:
{                                 (json object)
  "wallet_name" : "str",          (string) Name of wallet this operation was performed on
  "descriptors" : [               (json array) Array of descriptor objects
    {                             (json object)
      "desc" : "str",             (string) Descriptor string representation
      "timestamp" : n,            (numeric) The creation time of the descriptor
      "active" : true|false,      (boolean) Activeness flag
      "internal" : true|false,    (boolean, optional) Whether this is an internal or external descriptor; defined only for active descriptors
      "range" : [                 (json array, optional) Defined only for ranged descriptors
        n,                        (numeric) Range start inclusive
        n                         (numeric) Range end inclusive
      ],
      "next" : n                  (numeric, optional) The next index to generate addresses from; defined only for ranged descriptors
    },
    ...
  ]
}

object JsonSerializers {

implicit val descriptorsClassReads: Reads[descriptorsClass] =
    Json.reads[descriptorsClass]

  implicit val listDescriptorsReads: Reads[listDescriptorsResult] =
    Json.reads[listDescriptorsResult]
sealed abstract class WalletResult

case class listDescriptorsResult(
    wallet_name: String,
    descriptors: Vector[descriptorsClass]
) extends WalletResult

case class descriptorsClass(
    desc: String,
    timestamp: ZonedDateTime,
    active: Boolean,
    internal: Boolean,
    range: Array[(Int, Int)],
    next: Int
) extends WalletResult
1

There are 1 answers

1
sparker On

Here is a working example without the ZonedDateTime. It is probably better to have it represented as a Long in case class and convert it to ZonedDateTime later only when you need to represent it as ZonedDateTime

import play.api.libs.json.{Json, Reads, Writes}

sealed abstract class WalletResult

case class ListDescriptorsResult(
                                  wallet_name: String,
                                  descriptors: Seq[DescriptorsClass]
                                ) extends WalletResult

case class DescriptorsClass(
                             desc: String,
                             timestamp: Long,
                             active: Boolean,
                             internal: Boolean,
                             range: (Int, Int),
                             next: Int
                           ) extends WalletResult


implicit val descriptorsClassReads: Reads[DescriptorsClass] = 
  Json.reads[DescriptorsClass]
implicit val listDescriptorsResultReads: Reads[ListDescriptorsResult] = 
  Json.reads[ListDescriptorsResult]

val jsonString =
  """
    |{
    |  "wallet_name" : "str",
    |  "descriptors" : [
    |    {
    |      "desc" : "desc1",
    |      "timestamp" : 1657920976,
    |      "active" : true,
    |      "internal" : true,
    |      "range" : [
    |        10,
    |        20
    |      ],
    |      "next" : 10
    |    },
    |    {
    |      "desc" : "desc2",
    |      "timestamp" : 1657920976,
    |      "active" : true,
    |      "internal" : false,
    |      "range" : [
    |        30,
    |        40
    |      ],
    |      "next" : 20
    |    }
    |  ]
    |}
    |
""".stripMargin.replaceAll("\n","")

val jsValue = Json.parse(jsonString)

println(Json.fromJson[ListDescriptorsResult] (jsValue).get)

Here is the code in a scastie editor