ScalaJson: анализ необязательных значений (по умолчанию) без параметра

Я хотел бы проанализировать файл Json, чтобы прочитать значения, которые могут быть предоставлены необязательно. На случай, если они не будут предоставлены, у меня есть значения по умолчанию, к которым можно вернуться.

Очевидно, что в этом случае конечным результатом будет то, что у меня обязательно будет значение под рукой: либо считанное из Json, либо значение по умолчанию. Однако, согласно моим текущим знаниям о ScalaJson (пожалуйста, поправьте меня, если я ошибаюсь неправильно), мне все равно придется использовать Option[T] для его хранения (поскольку он может быть недоступен непосредственно в файле Json). Другими словами, я считаю, что, несмотря на то, что я могу указать значение по умолчанию, его все равно придется обернуть в Option[T]

Есть ли способ прочитать необязательное значение (со значением по умолчанию) без необходимости оборачивать его внутрь Option[T]? Хочу заранее сообщить, что у меня нет необходимости (в обозримом будущем) записывать (сериализовать) мои данные в Json, мне нужно только прочитать их (deserialize) через Json.


Чтобы уточнить мой вопрос: -

Я использую автоматическое преобразование с использованием case classes , поэтому вместо использования этого case class с заданным reads converter

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

Я хотел бы использовать это case class

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

Можно ли написать read converter для этого case class с тем же источником Json? В качестве альтернативы, есть ли лучший выбор дизайна, который может полностью решить эту проблему?


I'm on

  • Scala 2.11.11
  • PlayFramework 2.6

person y2k-shubham    schedule 26.02.2018    source источник
comment
Какой именно выпуск Play-JSON вы используете. Как минимум 2.6.7 поддерживает значения по умолчанию в сгенерированном Reads: github.com/playframework/play-json/blob/2.6.7/play-json/shared/   -  person cchantep    schedule 26.02.2018
comment
Спасибо, @cchantep, надо было поискать повнимательнее. На самом деле есть довольно много вопросов по этому поводу, на которые уже были даны ответы; хотя это решение кажется наиболее подходящим для Play 2.6.x, как рассказал @Aliaksandr Kavalevich   -  person y2k-shubham    schedule 27.02.2018


Ответы (2)


Если вы используете SBT, вы можете включить в свой проект spray-json с помощью

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

Определить класс дела

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

Определите протокол для преобразования json.

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 MyProtocol._

Сценарий 1#

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

Сценарий 2#

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

Справочная ссылка: https://github.com/spray/spray-json

person Rumesh Krishnan    schedule 26.02.2018

Используйте jsoniter-scala, он имеет встроенную поддержку значений по умолчанию для полей класса case.

Добавить зависимость библиотеки:

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
)

Сгенерируйте кодек для вашего корневого типа и используйте его для разбора классов case со значениями по умолчанию:

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())
person Andriy Plokhotnyuk    schedule 26.02.2018