【问题标题】:Not able to parse Map with Enum to Json in Play Scala无法在 Play Scala 中使用 Enum 将 Map 解析为 Json
【发布时间】:2016-09-20 10:17:00
【问题描述】:

我们使用Scala 2.11.8Play 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 类解析为Json。我知道这需要为Enumeration 提供一个隐式格式化程序。

我找到了这个解决方案:https://stackoverflow.com/a/15489179/1549135 并应用了它。

提供这些隐式的伴随对象如下所示:

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 scala enums playframework-2.0


    【解决方案1】:

    请注意,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$1@65aebb67
    
    scala>  Json.toJson(Map(EnumA.MON -> "hello"))
    res2: play.api.libs.json.JsValue = [{"monday":"hello"}]
    

    【讨论】:

    • 但正如我所说,Enum 工作正常。但Map[Enum, String] 没有。
    • @Atais ...编辑了答案...您所期望的...请检查。希望这会有所帮助
    • 但是......你得到了我最初询问的完全相同的错误
    • @Atais ...请检查答案...。我给出了发生这种情况的原因。编辑后的答案中解释了原因
    • @Atais Map[String, CustomType] 可以转换为 Json,但 Map[CustomType, String] 不能直接转换为 Json,因为有效的 json 始终以字符串为键。
    【解决方案2】:

    我们和同事准备了一个泛型类,为Map[E &lt;: 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"))
        }
      }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-30
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-06
      • 2015-11-11
      相关资源
      最近更新 更多