Vishal
Vishal

Reputation: 1492

Convert Json with variable keys

Am getting a JSON in

 {
    "segment": {
        "134": "34",
        "154": "342"
    }
}

all I am trying to do is map the keys as value i.e convert it into format like

    {
    "segment ": [{
        "segmentationStrategyId ": 134,
        "segmentID ": 34
    }, {
        "segmentationStrategyId ": 154,
        "segmentID ": 342
    }]
}

Tried using json4s parser but as key are diff I cannot map it to case class.

Upvotes: 3

Views: 971

Answers (3)

Andriy Plokhotnyuk
Andriy Plokhotnyuk

Reputation: 7989

It is much easier and faster with jsoniter-scala.

Add library dependency:

libraryDependencies ++= Seq(
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "0.29.2" % Compile, 
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "0.29.2" % Provided // required only in compile-time
)

Define your data structures for input and output JSON:

case class Input(segment: mutable.LinkedHashMap[Int, Int])
case class OutputSegment(segmentationStrategyId: Int, segmentID: Int)
case class Output(segment: List[OutputSegment])

Generate codecs for your root case classes:

import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._
implicit val inCodec: JsonValueCodec[Input] = JsonCodecMaker.make[Input](CodecMakerConfig())
implicit val outCodec: JsonValueCodec[Output] = JsonCodecMaker.make[Output](CodecMakerConfig())

Read input, convert to output and serialize it:

val fis = new FileInputStream("/tmp/input.json")
val input = try readFromStream(fis) finally fis.close()
val output = Output(input.segment.map(kv => OutputSegment(kv._1, kv._2)))
val fos = new FileOutputStream("/tmp/output.json")
try writeToStream(output, fos) finally fos.close()

Upvotes: 0

Rumesh Krishnan
Rumesh Krishnan

Reputation: 443

You can use spary.json to convert the json. If you use SBT you can include spray-json in your project with

libraryDependencies += "io.spray" %%  "spray-json" % "1.3.3"

Define the case class for your input and output.

case class InputJson(segment: Map[String, String])
case class OutputSegment(segmentationStrategyId: Int, segmentId: Int)
case class OutputJson(segment: List[OutputSegment])

Define the protocol through convert case class to json and json to case class

import spray.json._
object MyProtocol extends DefaultJsonProtocol {
  implicit val inputJsonFormat = jsonFormat1(InputJson.apply)
  implicit val outputSegmentFormat = jsonFormat2(OutputSegment.apply)
  implicit val outputJsonFormat = jsonFormat1(OutputJson.apply)
}

Input json:

val jsonString: String =
    """{
      |    "segment": {
      |        "134": "34",
      |        "154": "342"
      |    }
      |}""".stripMargin

parse the json String into JsValue

val jsonVal: JsValue = jsonString.parseJson

convert the JsValue into Case Class

val jsonInput: InputJson = jsonVal.convertTo[InputJson]

Now, you can map the Map[String,String] segment into OutputSegment format.

val outputSegments: List[OutputSegment] = jsonInput.segment.flatMap {
    case (key, value) => Try(OutputSegment(key.toInt, value.toInt)).toOption
  }.toList

create the OutputJson and get the equivalent Json String.

val outputJson: String = OutputJson(outputSegments).toJson.prettyPrint

Output Json String:

{
  "segment": [{
    "segmentationStrategyId": 134,
    "segmentId": 34
  }, {
    "segmentationStrategyId": 154,
    "segmentId": 342
  }]
}

Final full code sample:

import spray.json._
import scala.util.Try

object Test
  extends App {

  // case class
  case class InputJson(segment: Map[String, String])
  case class OutputSegment(segmentationStrategyId: Int, segmentId: Int)
  case class OutputJson(segment: List[OutputSegment])

  // protocol for json conversion
  object MyProtocol extends DefaultJsonProtocol {
    implicit val inputJsonFormat = jsonFormat1(InputJson.apply)
    implicit val outputSegmentFormat = jsonFormat2(OutputSegment.apply)
    implicit val outputJsonFormat = jsonFormat1(OutputJson.apply)
  }

  // input json
  val jsonString: String =
    """{
      |    "segment": {
      |        "134": "34",
      |        "154": "342"
      |    }
      |}""".stripMargin

  import MyProtocol._

  val jsonVal: JsValue = jsonString.parseJson

  val jsonInput: InputJson = jsonVal.convertTo[InputJson]

  val outputSegments: List[OutputSegment] = jsonInput.segment.flatMap {
    case (key, value) => Try(OutputSegment(key.toInt, value.toInt)).toOption
  }.toList

  val outputJson: String = OutputJson(outputSegments).toJson.prettyPrint

  println(outputJson)
}

Reference link: https://github.com/spray/spray-json

Upvotes: 4

Bentaye
Bentaye

Reputation: 9756

I am not familiar with json4s but managed to write this out quickly, might help:

import org.json4s._
import org.json4s.native.JsonMethods._

case class Segment(segmentationStrategyId: Int, segmentId: Int)

object Test {
  implicit val formats: DefaultFormats.type = DefaultFormats
  val json: String =
    """
      | {
      |    "segment": {
      |        "134": "34",
      |        "154": "342"
      |    }
      |}
    """.stripMargin

  def main(args: Array[String]): Unit = {
    val originalSegments = for {
      JObject(o) <- parse(json)
      JField("segment", JObject(segment))  <- o
    } yield segment

    val originalSegment = originalSegments.head
    val newSegments = originalSegment.map(
      s => Segment(s._1.toInt, s._2.extract[String].toInt)
    )
    println(newSegments)
  }
}

Prints out

List(Segment(134,34), Segment(154,342))

So you now have a list of case classes that you should be able to ransform into your JSON

Upvotes: 2

Related Questions