无法在 Play Scala 中使用 Enum 将 Map 解析为 Json
Not able to parse Map with Enum to Json in Play Scala
我们使用Scala 2.11.8
和Play framework 2.5.8
要处理的数据可以这么简单:
object EnumA extends Enumeration {
type EnumA = Value
val ONE, TWO, THREE = Value
}
case class NoWork(data: Map[EnumA.Value, String] = Map.empty)
我想要存档的是能够将 NoWork
class 解析为 Json
。我知道它需要为 Enumeration
提供隐式格式化程序。
我找到了这个解决方案: 并应用了它。
提供这些隐式的伴生对象如下所示:
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
类型修复 Map
?
谢谢!
编辑
作为帕姆的回答
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]
。
请注意,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(_))
}
}
Scala 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]] = $anon@65aebb67
scala> Json.toJson(Map(EnumA.MON -> "hello"))
res2: play.api.libs.json.JsValue = [{"monday":"hello"}]
我们与同事一起准备了一个通用 class,它为 Map[E <: Enum[E], T]
类型提供 JSON 序列化。
Enum
类型始终转换为 String
,因为 JsObject
key
需要它。另一个参数是通用的,使用 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"))
}
}
}
我们使用Scala 2.11.8
和Play framework 2.5.8
要处理的数据可以这么简单:
object EnumA extends Enumeration {
type EnumA = Value
val ONE, TWO, THREE = Value
}
case class NoWork(data: Map[EnumA.Value, String] = Map.empty)
我想要存档的是能够将 NoWork
class 解析为 Json
。我知道它需要为 Enumeration
提供隐式格式化程序。
我找到了这个解决方案: 并应用了它。
提供这些隐式的伴生对象如下所示:
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
类型修复 Map
?
谢谢!
编辑
作为帕姆的回答
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]
。
请注意,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(_))
}
}
Scala 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]] = $anon@65aebb67
scala> Json.toJson(Map(EnumA.MON -> "hello"))
res2: play.api.libs.json.JsValue = [{"monday":"hello"}]
我们与同事一起准备了一个通用 class,它为 Map[E <: Enum[E], T]
类型提供 JSON 序列化。
Enum
类型始终转换为 String
,因为 JsObject
key
需要它。另一个参数是通用的,使用 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"))
}
}
}