0

I am attempting to parse the following Json with json4s in Scala, but I cannot due to the nested structure:

[
 {
    "body":"8",
    "start":29,
    "value":{
        "value":8,
        "type":"value"
        },
    "end":30,
    "dim":"number",
    "latent":false
 },
 {
    "body":"2",
    "start":42,
    "value":{
        "value":2,
        "type":"value"
        },
    "end":43,
    "dim":"number",
    "latent":false
 }
]

With the following code, I can only extract the first case class, but the nested classes are not extracted:

println(stdout)
val obs = parse(stdout.toString())
val obs2 = parse(stdout.toString()).extract[DucklingList]
println(obs2.list)

Here is the output of the above:

[0m[[0minfo[0m] [0m[{"body":"8","start":29,"value":{"value":8,"type":"value"},"end":30,"dim":"number","latent":false},{"body":"2","start":42,"value":{"value":2,"type":"value"},"end":43,"dim":"number","latent":false}][0m
[0m[[0minfo[0m] [0mList(JObject(List((body,JString(8)), (start,JInt(29)), (value,JObject(List((value,JInt(8)), (type,JString(value))))), (end,JInt(30)), (dim,JString(number)), (latent,JBool(false)))), JObject(List((body,JString(2)), (start,JInt(42)), (value,JObject(List((value,JInt(2)), (type,JString(value))))), (end,JInt(43)), (dim,JString(number)), (latent,JBool(false)))))[0m
[0m[[0minfo[0m] [0mJObject(List((value,JInt(8)), (type,JString(value))))[0m
[0m[[0minfo[0m] [0mDucklingList(List(JObject(List((body,JString(8)), (start,JInt(29)), (value,JObject(List((value,JInt(8)), (type,JString(value))))), (end,JInt(30)), (dim,JString(number)), (latent,JBool(false)))), JObject(List((body,JString(2)), (start,JInt(42)), (value,JObject(List((value,JInt(2)), (type,JString(value))))), (end,JInt(43)), (dim,JString(number)), (latent,JBool(false))))))[0m

I have attempted to extract it using the json4s extract method with the case classes and serializers listed below.

case class DucklingValue(

    value: Int,
    typ: String
  )

  case class DucklingEntity(
    body: String,
    start: Int,
    end: Int,
    value: List[JField],
    dim: String,
    latent: Boolean
  )

  case class DucklingList(
    list: List[JValue]
  )

class DucklingEntitySerializer extends CustomSerializer[DucklingEntity](format => (
  {
    case JObject(
      JField("body", JString(body))
      :: JField("start", JInt(start))
      :: JField("end", JInt(end))
      :: JField("value", JObject(value))
      :: JField("dim", JString(dim))
      :: JField("latent", JBool(latent))
      :: Nil
    ) => DucklingEntity(body, start.toInt, end.toInt, value, dim, latent)
  },
  {
    case duckling_entity: DucklingEntity =>
      JObject(
        JField("body", JString(duckling_entity.body))
        :: JField("start", JInt(duckling_entity.start))
        :: JField("end", JInt(duckling_entity.end))
        :: JField("value", JObject(duckling_entity.value))
        :: JField("dim", JString(duckling_entity.dim))
        :: JField("latent", JBool(duckling_entity.latent))
        :: Nil
      )
  }
))

class DucklingValueSerializer extends CustomSerializer[DucklingValue](format => (
  {
    case JObject(
      JField("value", JInt(value))
      :: JField("type", JString(typ))
      :: Nil
    ) => DucklingValue(value.toInt, typ)
  },
  {
    case duckling_value: DucklingValue =>
      JObject(
        JField("value", JInt(duckling_value.value))
        :: JField("type", JString(duckling_value.typ))
        :: Nil
      )
  }
))


class DucklingListSerializer extends CustomSerializer[DucklingList](format => (
  {
    case JArray(list) => DucklingList(list)
  },
  {
    case duckling_list: DucklingList =>
      JArray(duckling_list.list)
  }
))

How can I get the nested serialized case class DucklingEntity to also be extracted under DucklingList?

Chris
  • 99
  • 1
  • 1
  • 14

1 Answers1

0

json4s will recursively parse nested objects so you should not need a custom serializer.

The problem is that you have put JSON types (JValue and JField) inside your deserialized classes when you should just put the appropriate case classes. Here are modified versions of your classes that should parse without any custom serializer:

case class DucklingValue(
  value: Int,
  typ: String
)

case class DucklingEntity(
  body: String,
  start: Int,
  end: Int,
  value: DucklingValue,
  dim: String,
  latent: Boolean
)

case class DucklingList(
  list: List[DucklingEntity]
)

Also note that your deserializers are restrictive because they require the fields to come in the specific order that you specify. It is better to extract the individual fields, like this:

case obj: JObject =>
  DucklingValue(
    (obj \ "value").Extract[Int],
    (obj \ "type").Extract[String]
  )

This also allows the fields to be in either order. Using this approach also allows you to deal with optional fields etc. which a simple match expression can't.

Tim
  • 26,753
  • 2
  • 16
  • 29