2016-09-20 74 views
1

我们使用Scala 2.11.8Play framework 2.5.8无法在播放与枚举解析映射到JSON斯卡拉

数据一起工作可以如此简单:

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

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

我要存档什么是能够将NoWork类解析为Json。我知道它需要为Enumeration提供一个隐式格式化程序。

我发现此解决方案:https://stackoverflow.com/a/15489179/1549135并应用它。

提供这些implicits同伴对象如下所示:

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

    implicit val jsonModelFormat = Json.format[NoWork] 
} 

,它总是失败,出现错误:

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

问题是什么?

我已经测试并将data类型更改为Map[String, String]允许序列化。 Enum本身也是可以序列化的,所以现在 - 如何修复与Enum类型?

谢谢!

编辑

由于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) 
} 

显然为工作这个情况,我真的需要其他Map[Enum, T],我可以在整个应用程序中使用一个通用的解决方案。

回答

0

随着我们已经准备了一个通用类,它提供JSON序列为Map[E <: Enum[E], T]类型的同事。

Enum类型始终转换为String,因为它是JsObjectkey所必需的。另一个参数是通用的,使用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

请注意,Json键必须是字符串。

下面的代码工作

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

下面的代码无法正常工作,因为有效的JSON重点应始终是字符串。这里的关键是EnumA.Value这不是String

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")) 

但如果你想让它按预期工作提供了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) 
    } 

现在下面的代码工作

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

可以为EnumA声明格式如下

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(_)) 
    } 
    } 

斯卡拉REPL输出

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

转换但是,当我告知,'Enum'工作正常。但'Map [Enum,String]'没有。 – Atais

+0

@Atais ...用你期待的内容编辑答案......请检查。希望这有助于 – pamu

+0

,但是......你得到了与我最初询问的 – Atais