Как написать конвертер записей Play JSON для класса case с одним членом, допускающим значение NULL

В Play 2.3 у меня есть класс case с одним необязательным двойным членом:

case class SomeClass(foo: Option[Double])

Мне нужен преобразователь записи JSON, который обрабатывает член как обнуляемый:

implicit val someClassWrite: Writes[SomeClass] = ???

В документах Play приведен пример:

case class DisplayName(name:String)
implicit val displayNameWrite: Writes[DisplayName] = Writes {
  (displayName: DisplayName) => JsString(displayName.name)
}

Но, к сожалению, я не могу понять, как это сделать для 1) одиночного нулевого значения и 2) двойного. Любые идеи? Спасибо.

Обновление №1. Единственное решение, которое я могу предложить, это:

implicit val someClassWrite: Writes[SomeClass] = Writes {
  (someClass: SomeClass) => someClass.foo match {
    case Some(f) => JsNumber(BigDecimal(f))
    case _ => JsNull
}

Обновление №2: игнорировать мое решение. Трэвис Браун - тот самый.


person Lasf    schedule 20.10.2014    source источник
comment
Все, что делает пример play docs, — это преобразование String в JsString, аналога которому для Option[Double] в JSON нет. Не могли бы вы предоставить больше контекста?   -  person Michael Zajac    schedule 20.10.2014
comment
@LimbSoup См. те же документы: case class DisplayName(name:String) val nameReads: Reads[DisplayName] = (JsPath \ "name").read[String].map(DisplayName(_)). Это отлично работает для преобразователя чтения, используя вместо этого .readNullable[Double]. Я ищу аналогичный конвертер записи.   -  person Lasf    schedule 20.10.2014
comment
@LimbSoup Я хочу... value map is not a member of play.api.libs.json.OWrites[Option[Double]]   -  person Lasf    schedule 20.10.2014
comment
Что вы пытаетесь отобразить? Как я уже сказал, нужно больше контекста.   -  person Michael Zajac    schedule 20.10.2014
comment
@LimbSoup Я не знаю, потому что я не знаком с тем, как работает Plays ORreads / OWrites. Я просто следую документам, которые работают в случае чтения, но не в случае записи. Я ценю вашу помощь в этом; это эзотерический вопрос о библиотеках Play, и, таким образом, контекст лежит в этих библиотеках Play, поэтому для того, чтобы я предоставил вам больше контекста, мне потребовалось бы более глубокое знакомство с тем, как работают библиотеки Play Json, что полностью моя проблема.   -  person Lasf    schedule 20.10.2014


Ответы (2)


Writes не является ковариантным функтором, поэтому вы не можете использовать map, но можете использовать contramap:

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val someClassWrites: Writes[SomeClass] =
  (__ \ 'foo).writeNullable[Double].contramap(_.foo)

Если у вас более одного участника, вы можете использовать синтаксис Play FunctionalBuilder:

case class AnotherClass(foo: Option[Double], bar: Option[String])

implicit val anotherClassWrites: Writes[AnotherClass] = (
  (__ \ 'foo).writeNullable[Double] and
  (__ \ 'bar).writeNullable[String]
)(ac => (ac.foo, ac.bar))

В первом случае аргумент contramap — это просто функция от типа, для которого вы хотите получить Writes, до типа Writes, для которого вы вызываете contramap. Во втором случае функция в конце идет от цели (здесь AnotherClass) к кортежу из Writes экземпляров, которые вы создали с помощью and (в данном случае Option[Double] и Option[String]).

person Travis Brown    schedule 20.10.2014
comment
это где-нибудь задокументировано? Я не могу найти его и борюсь с этим больше, чем это необходимо, лол. бит contramap, который - person Mark; 03.08.2018

Простой способ:

import play.api.libs.json.Json

implicit val fmt = Json.format[SomeClass]

Который использует макрос для автоматического создания формата json для вас. Beats реализует запись напрямую.

person triggerNZ    schedule 20.10.2014
comment
Я должен добавить, что это часть сложной системы записи... рекурсивной записи и т. д. Есть ли трудный путь? - person Lasf; 20.10.2014
comment
Если свойство вашего класса case равно someId:String, а элемент JSON, содержащий ваш someId, имеет значение some-ID, то вы не можете использовать формат, сгенерированный макросом. - person Peter Perháč; 30.01.2017