Skip to content

Instantly share code, notes, and snippets.

@pvoznenko
Last active May 11, 2018 19:25
Show Gist options
  • Save pvoznenko/a2f4a2b82d7bd7c67e0c to your computer and use it in GitHub Desktop.
Save pvoznenko/a2f4a2b82d7bd7c67e0c to your computer and use it in GitHub Desktop.
How to loop through json object and change values with circe, scala

How to go through json object and change values with Circe in Scala

Small gist with some demo trait that shows an example on how to loop through json object and change values with circe in Scala.

Code example you can find below at circe-loop-json-object-example.scala.

The input JSON for test is kinda in in DynamoDB dump format:

{
  "comment" : {"s" : "something"},
  "lat" : {"n" : "10.12312421"},
  "price" : {"n" : "123"},
  "third" : "normal"
}

And with normalization it will parse each value object and cast to the primitives. Output will be like this:

{
  "comment" : "something",
  "lat" : 10.12312421,
  "price" : 123,
  "third" : "normal"
}

Hopefully this gist can be used as example how to traverse through the json object and change values by using circe.

import cats.data.Xor._
import io.circe.Decoder.Result
import io.circe.Json._
import io.circe._, io.circe.generic.auto._, io.circe.jawn._, io.circe.syntax._, cats.data.Xor
trait DynamoDBJsonNormalization extends FieldParserHelper {
def updateField(json: Json): Json = transform(json)
def traverseThroughFields(json: Json, field: String): Json =
json.hcursor.downField(field).withFocus(updateField).top match {
case Some(result) => result
case None => json
}
def normalize(json: String): String = {
val doc = parse(json).getOrElse(empty)
val fields = doc.hcursor.fields.getOrElse(List())
fields.foldLeft(doc)(traverseThroughFields).toString
}
}
trait FieldParserHelper {
sealed trait DynamoDBValue
case class DynamoDBString(s: String) extends DynamoDBValue
case class DynamoDBNumber(n: String) extends DynamoDBValue
implicit class NumberChecker(number: Double) {
def isInteger = (number % 1) == 0
}
val isString: PartialFunction[Json, Result[DynamoDBValue]] = {
case json if json.as[DynamoDBString].isRight => json.as[DynamoDBString]
}
val isNumber: PartialFunction[Json, Result[DynamoDBValue]] = {
case json if json.as[DynamoDBNumber].isRight => json.as[DynamoDBNumber]
}
val isOther: PartialFunction[Json, Result[DynamoDBValue]] = {
case json => Right(DynamoDBString(json.asString.getOrElse("")))
}
val toStringJson: PartialFunction[Result[DynamoDBValue], Json] = {
case Right(DynamoDBString(string)) => Json.string(string)
}
val toDouble: PartialFunction[Result[DynamoDBValue], Double] = {
case Right(DynamoDBNumber(number)) => number.toDouble
}
val doubleToJson: PartialFunction[Double, Json] = {
case double => number(double).getOrElse(numberOrNull(0.0))
}
val toIntJson: PartialFunction[Double, Json] = {
case number if number.isInteger => int(number.toInt)
}
val defineCurrentType: PartialFunction[Json, Result[DynamoDBValue]] = isString orElse isNumber orElse isOther
val toNumberJson: PartialFunction[Result[DynamoDBValue], Json] = toDouble andThen (toIntJson orElse doubleToJson)
val castToPrimitive: PartialFunction[Result[DynamoDBValue], Json] = toStringJson orElse toNumberJson
val transform: PartialFunction[Json, Json] = defineCurrentType andThen castToPrimitive
}
object Test extends DynamoDBJsonNormalization {
val json =
"""
{
"comment":{"s":"something"},
"lat":{"n":"10.12312421"},
"price":{"n":"123"},
"third":"normal"
}
"""
def run = normalize(json)
}
Test.run
//res0: String = {
// "comment" : "something",
// "lat" : 10.12312421,
// "price" : 123,
// "third" : "normal"
//}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment