Ответ 1
Часто вы знаете, что кодер всегда будет создавать объект JSON (в отличие от произвольного значения JSON). Отслеживание этого факта в системе типов позволяет работать с выходом такого кодировщика, не перескакивая через обручи, которые обычно были бы необходимы.
Например, предположим, что у нас есть простой класс:
class Foo(val name: String, val age: Long)
И мы пишем пример Writes
следующим образом:
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val fooWrites: Writes[Foo] = (
(__ \ 'name).write[String] and (__ \ 'age).write[Long]
)(foo => (foo.name, foo.age))
Теперь мы можем написать следующее:
scala> val json = fooWrites.writes(new Foo("McBar", 101))
json: play.api.libs.json.JsValue = {"name":"McBar","age":101}
Теперь предположим, что по какой-либо причине мы хотим получить список имен полей. Мы должны написать что-то вроде этого:
scala> json.as[JsObject].keys
res0: scala.collection.Set[String] = Set(name, age)
Вместо этого:
scala> json.keys
<console>:17: error: value keys is not a member of play.api.libs.json.JsValue
json.keys
^
Но, конечно, мы знаем, что json
всегда будет JsObject
. Проблема в том, что компилятор этого не делает. OWrites
исправляет это.
implicit val fooWrites: OWrites[Foo] = (
(__ \ 'name).write[String] and (__ \ 'age).write[Long]
)(foo => (foo.name, foo.age))
И затем:
scala> val json = fooWrites.writes(new Foo("McBar", 101))
json: play.api.libs.json.JsObject = {"name":"McBar","age":101}
scala> json.keys
res1: scala.collection.Set[String] = Set(name, age)
Вывод Writes
на OWrites
статически вводится как JsObject
, поэтому мы можем использовать .keys
без небезопасного as[JsObject]
.
(В качестве побочного примечания я не лично поклонник, который делает типы возвращаемых типов более конкретными в подклассах, и я взял несколько иной подход к решению этой проблемы в circe.)