Play JSON Reads: чтение значения, которое может быть представлено в нескольких типах - PullRequest
0 голосов
/ 20 февраля 2020

У меня есть такие JSON для ship:

{
  "name" : "Watership Elizbeth",
  "sea": "white",
  "size": 100500,
  "residents" : [ {
    "type" : "men",
    "age" : 4,
    "sex": 0
  }, {
    "type" : "cat",
    "legs" :4,
    "color" : "black",
    "leg1" :{
      "color" : "black"
    },
    "leg2" :{
      "color" : "white"
    },
    "leg3" :{
      "color" : "brown"
    },
    "leg4" :{
      "color" : "black"
    }
  },{
    "type" : "bird",
    "size" : "XXL",
    "name": "ostrich"
  }, {"type": "water",...}, {"type": "mice",...}, {...}]
}

Как вы видите в JsArray residents У меня есть жители, которые отличаются: men, cat, bird, ... water, ... Все, что жители ship имеют собственные простые классы дел в Scala код:

case class Men (type: String, age: Int, sex: Int)
case class Leg (color: String)
case class Cat (type: String, legs: Int, color: String, leg1: Leg, leg2: Leg, leg3: Leg, leg4: Leg)
case class Bird (...)

Также для каждого простого класса дел я пишу readers в своих сопутствующие объекты:

object Men {
implicit val reads: Reads[Ship] = (
    (JsPath \ "type").read[String] and
      (JsPath \ "age").read[Int] and
      (JsPath \ "sex").read[Int]
    )(Ship.apply _)
}
// and so on for another objects...

Основная проблема в том, что я не могу найти, как создать класс case для Ship и его reads:

case class Ship (name: String, sea: String, size: Int, residents: Seq[???])

object Ship {
implicit val reads: Reads[Ship] = (
    (JsPath \ "name").read[String] and
      (JsPath \ "sea").read[String] and
      (JsPath \ "size").read[Int] and
      (JsPath \ "residents").read[Seq[???]]
    )(Ship.apply _)
}

Как видите residents это массив, элементы которого могут быть разных типов: Men, Cat, Bird .. Я хочу вместо этого ??? поместить в чтение Ship все объекты моих типов или некоторые подобные `AnyVal, но это не так работа:

// for case classes
case class Ship (name: String, sea: String, size: Int, residents: Seq[Man,Cat,Bird,...])
case class Ship (name: String, sea: String, size: Int, residents: Seq[AnyVal])

// for case reads
(JsPath \ "residents").read[Seq[Man,Cat,Bird,...]]
(JsPath \ "residents").read[Seq[AnyVal]]

Как написать регистр класса Ship и его reads, который может читать в своем ключе несколько типов?

1 Ответ

1 голос
/ 24 февраля 2020

Самый простой способ - расширить классы дел из запечатанной черты. Таким образом, play- json может автоматически генерировать чтение:

sealed trait Resident

case class Men (age: Int, sex: Int) extends Resident
case class Leg (color: String) extends Resident
case class Cat (legs: Int, color: String, leg1: Leg, leg2: Leg, leg3: Leg, leg4: Leg) extends Resident
case class Bird (...) extends Resident

object Resident {
  private val cfg = JsonConfiguration(
      discriminator = "type",

      typeNaming = JsonNaming { fullName =>
        fullName
          .replaceFirst(".*[.]", "")
          .toLowerCase
      }
    )

  implicit val reads = Json.configured(cfg).reads[Resident]
}

case class Ship (name: String, sea: String, size: Int, residents: Seq[Resident])

object Ship {
  implicit val reads: Reads[Ship] = Json.reads[Ship]
}

Здесь - полный пример.

...