2015-02-26 2 views
2

Я генерирую JSON для скорости, где единиц может отличаться. У меня есть черта SpeedUnit и классы, которые расширяют ее (узлы, метрыPerSecond, MilesPerHour). JSON Play documentation сказал: «Чтобы преобразовать ваши собственные модели в JsValues, вы должны определить неявные Writes-конвертеры и предоставить их в области видимости». Я получил это, чтобы работать в большинстве мест, но не тогда, когда у меня был класс, расширяющий черту. Что я делаю не так? Или есть вариант Enum, который я мог или должен был использовать вместо этого?Получение JSON JsValueWrapper для класса, который расширяет черту

// Type mismatch: found (String, SpeedUnit), required (String, Json.JsValueWrapper) 
// at 4th line from bottom: "speedunit" -> unit 

import play.api.libs.json._ 

trait SpeedUnit { 
    // I added this to SpeedUnit thinking it might help, but it didn't. 
    implicit val speedUnitWrites = new Writes[SpeedUnit] { 
    def writes(x: SpeedUnit) = Json.toJson("UnspecifiedSpeedUnit") 
    } 
} 

class Knots extends SpeedUnit { 
    implicit val knotsWrites = new Writes[Knots] { 
    def writes(x: Knots) = Json.toJson("KT") 
    } 
} 
class MetersPerSecond extends SpeedUnit { 
    implicit val metersPerSecondWrites = new Writes[MetersPerSecond] { 
    def writes(x: MetersPerSecond) = Json.toJson("MPS") 
    } 
} 
class MilesPerHour extends SpeedUnit { 
    implicit val milesPerHourWrites = new Writes[MilesPerHour] { 
    def writes(x: MilesPerHour) = Json.toJson("MPH") 
    } 
} 

// ... 

class Speed(val value: Int, val unit: SpeedUnit) { 
    implicit val speedWrites = new Writes[Speed] { 
    def writes(x: Speed) = Json.obj(
     "value" -> value, 
     "speedUnit" -> unit // THIS LINE DOES NOT TYPE-CHECK 
    ) 
    } 
} 

ответ

3

Writes является примером класса типа, который означает, что вам нужен один экземпляр Writes[A] для данного A, а не для каждого A экземпляра. Если вы исходите из фона Java, подумайте Comparator вместо Comparable.

import play.api.libs.json._ 

sealed trait SpeedUnit 
case object Knots extends SpeedUnit 
case object MetersPerSecond extends SpeedUnit 
case object MilesPerHour extends SpeedUnit 

object SpeedUnit { 
    implicit val speedUnitWrites: Writes[SpeedUnit] = new Writes[SpeedUnit] { 
    def writes(x: SpeedUnit) = Json.toJson(
     x match { 
     case Knots => "KTS" 
     case MetersPerSecond => "MPS" 
     case MilesPerHour => "MPH" 
     } 
    ) 
    } 
} 

case class Speed(value: Int, unit: SpeedUnit) 

object Speed { 
    implicit val speedWrites: Writes[Speed] = new Writes[Speed] { 
    def writes(x: Speed) = Json.obj(
     "value" -> x.value, 
     "speedUnit" -> x.unit 
    ) 
    } 
} 

И потом:

scala> Json.toJson(Speed(10, MilesPerHour)) 
res0: play.api.libs.json.JsValue = {"value":10,"speedUnit":"MPH"} 

Я поставил Writes экземпляры в компаньона объектов для двух типов, но они могут пойти в другом месте (если вы не хотите смешивать проблемы сериализации в вашей модели, например).

Вы также можете упростить (или, по меньшей мере, лаконичным-римента) это много с функциональным API Play JSON в:

sealed trait SpeedUnit 
case object Knots extends SpeedUnit 
case object MetersPerSecond extends SpeedUnit 
case object MilesPerHour extends SpeedUnit 

case class Speed(value: Int, unit: SpeedUnit) 

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

implicit val speedWrites: Writes[Speed] = (
    (__ \ 'value).write[Int] and 
    (__ \ 'speedUnit).write[String].contramap[SpeedUnit] { 
    case Knots => "KTS" 
    case MetersPerSecond => "MPS" 
    case MilesPerHour => "MPH" 
    } 
)(unlift(Speed.unapply)) 

Какой подход вы принимаете (функциональный или неявное) во многом дело вкуса.

+1

Большое спасибо @TravisBrown, это выглядит действительно здорово. Мне нравятся функциональные подходы. Оба ваших решения выглядят неплохо. Если они сработают, я вернусь и приму этот ответ. – gknauth

+0

Это действительно работало @TravisBrown. Опять же, это был прекрасный ответ, очень полезный и образовательный. – gknauth

Смежные вопросы