2016-09-20 4 views
1

Wir Scala 2.11.8 verwenden Karte mit Enum zu Json in Spielen zu analysieren und Play framework 2.5.8Nicht in der Lage Scala

Daten mit arbeiten kann so einfach sein:

object EnumA extends Enumeration { 
    type EnumA = Value 
    val ONE, TWO, THREE = Value 
} 

case class NoWork(data: Map[EnumA.Value, String] = Map.empty) 

Und was ich zu archivieren wollen, ist in der Lage sein, die Klasse NoWork zu Json zu analysieren. Ich weiß, dass dazu ein impliziter Formatierer für Enumeration bereitgestellt werden muss.

Ich habe diese Lösung gefunden: https://stackoverflow.com/a/15489179/1549135 und angewendet es.

Das Objekt Begleiter diese implicits Bereitstellung sieht wie folgt aus:

object NoWork { 
    implicit val enumAFormat = EnumUtils.enumFormat(EnumA) 

    implicit val jsonModelFormat = Json.format[NoWork] 
} 

Und es nicht immer mit einem Fehler:

error: No implicit format for Map[EnumA.Value,String] available. 
     implicit val jsonModelFormat = Json.format[NoWork] 
               ^

Was ist das Problem?

Ich habe den data Typ getestet und geändert, um Map[String, String] Serialisierung ermöglicht. Die Enum für sich ist auch serialisierbar, so - wie nun die Map mit Enum Typ zu beheben?

Danke!

bearbeiten

Als Antwort der Pamu

implicit val writes = new Writes[Map[EnumA.Value, String]] { 
    override def writes(o: Map[EnumA.Value, String]): JsValue = Json.toJson(o.map { case (a, b) => Json.parse(s"""{${Json.toJson(a)}:${Json.toJson(b)}}""")}.toList) 
} 

für diese Situation klar funktionieren würde, müsste ich eigentlich eine generische Lösung für andere Map[Enum, T], die ich in ganzer Anwendung nutzen könnte.

Antwort

0

Mit Kollegen wir eine generische Klasse vorbereitet haben, die JSON-Serialisierung für Map[E <: Enum[E], T] Typ bietet.

Der Enum Typ ist immer String umgewandelt, wie es für JsObjectkey erforderlich ist. Der andere Parameter ist generisch und wird mit dem implicit format: Format[T]

import play.api.data.validation.ValidationError 
import play.api.libs.json._  
import scala.util.{Failure, Success, Try} 

class MapEnumFormat[E <: Enum[E], T](valueOf: (String => E))(implicit format: Format[T]) extends Format[Map[E, T]] { 

    override def writes(o: Map[E, T]): JsValue = { 
    JsObject(o.map { case (a, b) => (a.name, Json.toJson(b)) }) 
    } 

    override def reads(json: JsValue): JsResult[Map[E, T]] = { 
    val result = Try(json.as[Map[String, T]].map { 
     case (key, value) => 
     valueOf(key) -> value 
    }) 

    result match { 
     case Success(status) => 
     JsSuccess(status) 
     case Failure(th) => 
     JsError(ValidationError(s"Error while serializing $json: $th")) 
    } 
    } 

} 
2

Beachten Sie, dass JSON-Schlüssel zwingend Zeichenfolgen sein müssen.

Code Nach arbeitet

Json.toJson(Map("mon" -> EnumA.MON)) 

folgenden Code funktioniert nicht, weil Schlüssel für gültige Json immer Zeichenfolge sein sollte. Hier ist der Schlüssel EnumA.Value, der nicht String ist.

scala> Json.toJson(Map(EnumA.MON -> "mon")) 
    <console>:19: error: No Json serializer found for type scala.collection.immutable.Map[EnumA.Value,String]. Try to implement an implicit Writes or Format for this type. 
      Json.toJson(Map(EnumA.MON -> "mon")) 

Aber wenn man es wie erwartet arbeiten möchten bieten eine writes

implicit val writes = new Writes[Map[EnumA.Value, String]] { 
     override def writes(o: Map[EnumA.Value, String]): JsValue = Json.toJson(o.map { case (a, b) => Json.parse(s"""{${Json.toJson(a)}:${Json.toJson(b)}}""")}.toList) 
    } 

jetzt folgenden Code

Json.toJson(Map(EnumA.MON -> "hello")) 

funktioniert können Sie Format deklarieren für EnumA folgend

object EnumA extends Enumeration { 
    val MON = Value("monday") 
    val TUE = Value("Tuesday") 

    implicit val format = new Format[EnumA.Value] { 
     override def writes(o: EnumA.Value): JsValue = Json.toJson(o.toString) 
     override def reads(json: JsValue): JsResult[EnumA.Value] = json.validate[String].map(EnumA.withName(_)) 
    } 
    } 

Scala REPL Ausgang

scala>  object EnumA extends Enumeration { 
    |   val MON = Value("monday") 
    |   val TUE = Value("Tuesday") 
    | 
    |   implicit val format = new Format[EnumA.Value] { 
    |   override def writes(o: EnumA.Value): JsValue = Json.toJson(o.toString) 
    |   override def reads(json: JsValue): JsResult[EnumA.Value] = json.validate[String].map(EnumA.withName(_)) 
    |   } 
    |  } 
defined object EnumA 

scala> Json.toJson(EnumA.MON) 
res0: play.api.libs.json.JsValue = "monday" 

scala> (Json.parse("""{"a": "monday"}""") \ "a").validate[EnumA.Value] 
res7: play.api.libs.json.JsResult[EnumA.Value] = JsSuccess(monday,) 

scala> (Json.parse("""{"a": "monday"}""") \ "a").validate[EnumA.Value].get 
res10: EnumA.Value = monday 

scala> Json.toJson(Map("mon" -> EnumA.MON)) 
res2: play.api.libs.json.JsValue = {"mon":"monday"} 

scala> Json.toJson(Map(EnumA.MON -> "mon")) 
<console>:19: error: No Json serializer found for type scala.collection.immutable.Map[EnumA.Value,String]. Try to implement an implicit Writes or Format for this type. 
     Json.toJson(Map(EnumA.MON -> "mon")) 

scala> implicit val writes = new Writes[Map[EnumA.Value, String]] { 
    |  override def writes(o: Map[EnumA.Value, String]): JsValue = Json.toJson(o.map { case (a, b) => Json.parse(s"""{${Json.toJson(a)}:${Json.toJson(b)}}""")}.toList) 
    |  } 
writes: play.api.libs.json.Writes[Map[EnumA.Value,String]] = [email protected] 

scala> Json.toJson(Map(EnumA.MON -> "hello")) 
res2: play.api.libs.json.JsValue = [{"monday":"hello"}] 
+0

umgewandelt Aber wie ich sagte, die 'Enum' funktioniert gut. Aber 'Map [Enum, String]' nicht. – Atais

+0

@Atais ... bearbeitet die Antwort ... mit dem, was Sie erwarten .. Bitte überprüfen Sie. Hoffe, das hilft – pamu

+0

, aber ... Sie haben genau den gleichen Fehler, den ich ursprünglich gefragt habe über – Atais