JSON processing in scala

136 Views Asked by At

I am in the process of switching from json to avro format and all my unit tests are still expecting json. I can convert the newly avro files to json but the schema is embedded. For example:

val j = """{"field1": "someString", "field2": 1607359485}"""
// now becomes:
val jAvro = """{"field1": {"string":"someString"}, "field2": {"long":1607359485}}"""

I want, momentarily, to remove the schema from jAvro. Parsing it is not a problem:

import play.api.libs.json.{ JsObject, Json }
Json.parse(jAvro).as[JsObject]

But how can I programmatically remove all the schema information, so that I get the same outpout as Json.parse(j).as[JsObject]?

EDIT

Thanks for your answers. I forgot to mention that the level of intricacy might be superior to my example.

I could have:

val jAvro = """{"field1": {"string":"someString"}, "field2": {"long":1607359485}, "field3":{"SomeObject":{"subfield1":{"int":11},"subfield2":{"string":"someString2"}}}"""

and I would like to extract:

val j = """{"field1":"someString", "field2":1607359485, "field3":{"subfield1":11,"subfield2":"someString2"}}"""

2

There are 2 best solutions below

7
On

You can try something like:

def updateJson(json: JsObject): Map[String, Any] = json.value.view.mapValues(_.validate[JsObject] match {
  case JsSuccess(value, _) =>
    value.fields.headOption.map(_._2).map {
      case obj: JsObject =>
        updateJson(obj)
      case value: JsValue =>
        value
      case other =>
        ??? // Error handling
    }.getOrElse(???) // Error handling
  case JsError(errors) =>
    println("Do here error handling", errors)
    ???
}).toMap

Code run at Scastie.

0
On

You can use Reads.mapReads:

import play.api.libs.json._

implicit val valueAsObj: Reads[JsValue] = Reads[JsValue] {
  case JsObject(fields) => fields.headOption match {
    case Some((_/*type*/, value)) =>
      JsSuccess(value)

    case _ =>
      JsError("error.singlefield.expected")
  }

  case _ =>
    JsError("error.jsobject.expected")
}

val mapReads: Reads[Map[String, JsValue]] = Reads.mapReads(valueAsObj)
val objReads: Reads[JsObject] = mapReads.map(JsObject(_))

Then:

val jAvro = """{"field1": {"string":"someString"}, "field2": {"long":1607359485}}"""

Json.parse(jAvro).validate(objReads)
// JsSuccess({"field1":"someString","field2":1607359485},)