Play / Scala: карта сериализуется как массив - PullRequest
1 голос
/ 14 января 2020

У меня есть определенный класс KG (класс case и сопутствующий объект):

case class KG(a: Resource,
              b: String,
              c: Option[Resource],
              d: Option[String],
              d: Option[Seq[String]])

object KG {
  implicit val writes: Writes[KG] = (o: KG) => Json.obj(
    "a" -> o.resource.getURI,
    "b" -> o.label,
    "c" -> o.subClassOf.map(_.getURI),
    "d" -> o.d
  )
}

Это вывод моей консоли sbt при попытке выяснить, что происходит

test: KG = KG(http://some-url-this-is,test,Some(http://another-url-this-is),None,None)

scala> val ontology = Seq(test)
ontology: Seq[KG] = List(KG(http://some-url-this-is,test,Some(http://another-url-this-is),None,None))

scala> val initial = ontology.groupBy(_.c.map(_.getLocalName))
initial: scala.collection.immutable.Map[Option[String],Seq[KG]] = Map(Some(Person) -> List(KG(http://some-url-this-is,test,Some(http://another-url-this-is),None,None))

scala> initial.getClass
res8: Class[_ <: scala.collection.immutable.Map[Option[String],Seq[KG]]] = class scala.collection.immutable.Map$Map1

scala> Json.toJson(initial)
res7: play.api.libs.json.JsValue = [["another-url-this-is",[{"a":"http://some-url-this-is","b":"test","c":"http://another-url-this-is","d":null}]]]

Хотя initial является картой, она сериализуется как массив .... Почему это? Я неправильно настроил какие-либо последствия сериализации?

[
  [
    "another-url-this-is", 
    [
      {
        "d": null, 
        "b": "test", 
        "a": "http://some-url-this-is", 
        "c": "http://another-url-this-is"
      }
    ]
  ]
]

Ответы [ 2 ]

2 голосов
/ 14 января 2020

Короткий ответ: единственное Writes[Map[_,_]] Play JSON обеспечивает Map[String, V], поскольку они гарантированно сериализуются как объект JSON. Для всех других типов ключей тот факт, что Map[K,V] является Iterable[(K, V)] (если у вас есть некоторый опыт работы с Лиспом, это фактически похоже на «список ассоциаций»: список пар) означает, что значение по умолчанию Writes[Iterable[(K, V)]] вступает в силу. Это сериализует каждый (K, V) как массив JSON длины 2 с первым значением, равным K, а вторым - V; все эти массивы JSON являются значениями во внешнем массиве JSON.

Такое поведение обусловлено тем фактом, что, хотя значения в объекте JSON могут быть любыми значениями JSON, ключи должны быть строки. В конкретном случае Option[String] это не работает, поскольку нет строки JSON, которую можно использовать для представления None, которая не будет сталкиваться с Some, содержащим эту строку.

Play- JSON 2.8 действительно вводит KeyWrites классов типов для сериализации K в JsString. Однако в конкретном случае Option[String] это может не иметь большого значения, так как вам все равно нужно будет выбрать строку: если вы можете быть уверены, что эта строка никогда не появится, вы могли бы иметь KeyWrites, который взрывается с исключением, если вы пытаетесь сериализовать сталкивающуюся строку. Однако на стороне KeyReads это может привести к некоторым хитрым ошибкам, если вы получите полезную нагрузку JSON с этой строкой в ​​качестве ключа.

Решение Niko для получения Map[String,V] превосходно; Этот ответ более подробно объясняет, почему. Вы можете (при использовании Play- JSON 2.8) также рассмотреть возможность замены Option[String] на более значимый тип с ограничениями и затем определить KeyWrites для этого типа.

1 голос
/ 14 января 2020

Понятия не имею почему, но проблема здесь в том, что карта имеет тип Map[Option[String],Seq[KG]]. Если groupBy настроен для получения String s для ключей - вместо Option[String] s - тогда сериализация выполнена успешно, и объект Json фактически представляет карту. Поэтому, если код изменяется на следующее:

val theMap: Map[String, Seq[KG]] = ontology.groupBy(_.c match {
        case Some(someActualString) => someActualString.getLocalName
        case None => "SomethingEmpty"
      })

, результирующее Json будет следующим:

{
  "someKey": [
    {
      "d": null, 
      "b": "person", 
      "a": "http://someURL", 
      "c": "http://someOtherURL"
    }
  ]
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...