0

I'd like to parse Json file to read values that maybe optionally provided. In case they aren't provided, I have default values to fall-back on.

Clearly in this case, the end result is that I will certainly have a value at hand: either the one read from Json or the default. However as per my current knowledge of ScalaJson (please correct me if I'm wrong), I'll still have to use an Option[T] to hold it (because it may not be available directly in the Json file). In other words, I believe that while I can supply a default value, it will still have to be wrapped in an Option[T]

Is there a way to read an optional value (with default) without having to wrap it inside Option[T]? I'd like to inform in advance that I have no requirement (in the foreseeable future) to write (serialize) my data into Json, I only have to read it (deserialize) a Json.


To further elaborate my question:-

I'm using automatic-conversion using case classes, so instead of having to use this case class with the given reads converter

case class MyCaseClass(optString: Option[String] = Some("None"))
implicit val reads = Json.reads[MyCaseClass]

I'd like to use this case class

case class MyCaseClass(optStringWithDefault: String = "None")

Is it possible to write a read converter for this case class given the same Json source? Alternatively, is there a better design choice that can overcome this problem altogether?


I'm on

  • Scala 2.11.11
  • PlayFramework 2.6
y2k-shubham
  • 10,183
  • 11
  • 55
  • 131
  • 1
    Which Play-JSON release are you precisely using. At least 2.6.7 support the default values in the generated `Reads`: https://github.com/playframework/play-json/blob/2.6.7/play-json/shared/src/test/scala/play/api/libs/json/MacroSpec.scala#L290 – cchantep Feb 26 '18 at 16:47
  • Thanks **@cchantep**, I should've looked harder. Indeed there are quite a few questions on this that have already been answered; though this solution seems to be the most appropriate for `Play 2.6.x` as [told by @Aliaksandr Kavalevich](https://stackoverflow.com/a/44884127/3679900) – y2k-shubham Feb 27 '18 at 05:29

2 Answers2

0

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

case class MyCaseClass(optString: String = "None")

Define the protocol for json conversion.

import spray.json._
object MyProtocol extends DefaultJsonProtocol {
    implicit object MyCaseClassFormat extends RootJsonFormat[MyCaseClass] {
      def write(obj: MyCaseClass): JsValue = JsObject(
        "optString" -> JsString(obj.optString)
      )
      def read(json: JsValue): MyCaseClass = {
        json.asJsObject.getFields("optString") match {
          case Seq(JsString(optString)) => MyCaseClass(optString.asInstanceOf[String])
          case _ => MyCaseClass()
        }
      }
    }
  }

import the protocol

import MyProtocol._

Scenario 1#

import spray.json._
val jsonStringOne: String = """{"optString": "testData"}"""
val resultOne = jsonStringOne.parseJson.convertTo[MyCaseClass]
Output: MyCaseClass(testData)

Scenario 2#

import spray.json._
val jsonStringTwo: String = """{"otherString": "tempData"}"""
val resultTwo = jsonStringTwo.parseJson.convertTo[MyCaseClass]
Output: MyCaseClass(None)

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

Rumesh Krishnan
  • 443
  • 4
  • 16
0

Use jsoniter-scala, it has built-in support of default values for case class fields.

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
)

Generate codec for your root type and use it for parsing of case classes with default values:

import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._

case class Device(id: Int = 1, model: String = "iPhone X")

case class User(name: String = "Joe", devices: Seq[Device] = Seq(Device()))

implicit val codec: JsonValueCodec[User] = JsonCodecMaker.make[User](CodecMakerConfig())

val user = readFromArray("{}".getBytes)

require(user == User())
Andriy Plokhotnyuk
  • 7,883
  • 2
  • 44
  • 68