Convert Json with Circus

Assuming the following json payload

val json = """{ "choices" : [ { "name" : "A" }, { "name" : "B" }, { "name" : "C" }, { "name" : "D" } ], "domain" : "Quizz", "level" : "Test", "mandatory": true }""" 

How to convert it to

 val json = """{ "value":"B", "domain" : "Quizz", "level" : "Test", }""" 

where is "B" randomly selected from the available options?

This is what I got so far:

 val cursor = parse(json).getOrElse(Json.Null).cursor for{ noMandatory<- cursor.downField("mandatory").flatMap(_.delete).map(_.top) withEmptyValue = noMandatory.deepMerge(Json.obj("value"->Json.Null)) }yield withEmptyValue 

Discards an unused "required" field and inserts an empty "value" field. Getting a random value from an array and placing it in a β€œvalue” eludes me.

- edit

I tried using hcursor, which makes the above understandable (it doesn't matter to me)

 val cursor = parse(json).getOrElse(Json.Null).hcursor val noMandatory = cursor.downField("mandatory").delete val withEmptyValue = noMandatory.withFocus(_.deepMerge(Json.obj("value"->Json.Null))) 

(in the above examples I use circe 0.5.1)

+5
source share
3 answers

Do not mix side effects with clean code. Therefore, I did not solve your randomization problem.

Secondly, I would recommend not to delete the "election" field so that everything is simple. Delete this field later when you want.

Third, separate the stages of production and transformation for simplicity. Here you can use the case class to do most of the work.

I am sure that this may not be an idiomatic Circe solution, but a good Scala:

 import io.circe._, io.circe.generic.auto._, io.circe.parser._, io.circe.syntax._ object ChooserApp extends App { val input = """{ "choices" : [ { "name" : "A" }, { "name" : "B" }, { "name" : "C" }, { "name" : "D" } ], "domain" : "Quizz", "level" : "Test", "mandatory": true }""" val expected = """{ "choices" : [ { "name" : "A" }, { "name" : "B" }, { "name" : "C" }, { "name" : "D" } ], "value":"B", "domain" : "Quizz", "level" : "Test", "mandatory": true }""" case class ForJson(j: Json) { def choices: List[String] = { j.asObject .toList .flatMap(_ ("choices")) .flatMap(_.asArray) .flatten .flatMap(_.asObject) .flatMap(_ ("name")) .flatMap(_.asString) } def chosen(a: String): Json = { j.asObject .map(_.add("value", Json.fromString(a))) .map(Json.fromJsonObject) .getOrElse(j) } } val expectedList = List("A", "B", "C", "D") val gotList = ForJson(parse(input).toOption.get).choices assert(gotList == expectedList, s"Expected $expectedList, got $gotList") val expectedJson = parse(expected).toOption.get val gotJson = ForJson(parse(input).toOption.get).chosen("B") assert(gotJson == expectedJson, s"Expected $expectedJson, got $gotJson") } 
+2
source

After digging through the api, this is what I came up with:

 // a couple utilities def renameKey(srcKey:String,target:String )(obj:JsonObject):Json= { obj("name").fold(obj)(value => obj.add("value", value).remove("name")).asJson } def safeDropKeys(json:Json)(keys:String*):Json= json.withObject(obj=>keys.foldLeft(obj)((acc, s)=>acc.remove(s)).asJson) 

The random extractor I shared on the recommendation of @ scala -william:

 def extractRandomChip(cursor: HCursor): Xor[DecodingFailure, Json] = { val choice = cursor.get[Array[Json]]("choices").map { choices => val x = util.Random.nextInt(choices.length) choices(x).withObject(renameKey("name", "value")) } choice } 

Then we glue everything together:

  val json = for { initial <- parse(s) value <- extractRandomChip(initial.hcursor) cleanedUp = safeDropKeys(initial)("mandatory","choices") } yield cleanedUp.deepMerge(value) json.map(_.toString).toOption 

According to my comment regarding @wheaties answer, I will compare both options and accept the results of the fastest option.

+1
source

my 2cents

 @ for{ j <- parser.parse(json).toOption jObj <- j.asObject jChoices <- jObj("choices") choices <- jChoices.as[Seq[Map[String,Json]]].toOption v <- choices(Random.nextInt(choices.size)).get("name") } yield { jObj.add("value", v ) .remove("choices") } res58: Option[io.circe.JsonObject] = Some(object[domain -> "Quizz",level -> "Test",mandatory -> true,value -> "C"]) 
+1
source

All Articles