无法在Play Scala中使用Enum解析Map到Json

时间:2016-09-20 10:17:01

标签: json scala enums playframework-2.0

我们使用@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // TODO Auto-generated method stub super.onActivityResult(requestCode, resultCode, data); /*camera preview*/ if (resultCode != RESULT_CANCELED) { if (requestCode == 0) { Bitmap bp = (Bitmap) data.getExtras().get("data"); previewView.setImageBitmap(bp); } /*gallery preview*/ else { Uri uri = data.getData(); try { Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri); Log.d(TAG, String.valueOf(bitmap)); ImageView previewView = (ImageView) findViewById(R.id.imgPostIssue); previewView.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } } } } 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。我知道它需要为Json提供隐式格式化程序。

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

提供这些含义的伴侣对象如下所示:

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类型修复Map

谢谢!

修改

作为Pamu的回答

Enum

显然适用于情况,我实际上需要一个可以在整个应用程序中使用的其他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) } 的通用解决方案。

2 个答案:

答案 0 :(得分: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(_))
    }
  }

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"}]

答案 1 :(得分:1)

我们与同事一起准备了一个通用类,为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"))
    }
  }

}