为什么我无法将Map[String,Any]序列化为JSON?

3

我似乎找不到一种合适的方法将我的Map序列化为JSON对象,因为它的值有不同的类型,例如:

var user: Map[String, Any] = Map("name" -> "1", "id" -> 1)
val json: JsValue = Json.obj(
  "user" -> user
)

生成:

type mismatch;
 found   : Map[String,Any]
 required: play.api.libs.json.Json.JsValueWrapper

或者,如果我使用 Map[String,AnyVal],它会产生以下结果:

the result type of an implicit conversion must be more specific than AnyVal

但是如果我使用Map[String,Number]Map[String,String],它就可以工作。

我错过了什么,有没有一些包装器我可以调用来安全地在我的JSON.obj()调用中同时使用数字和整数?

1个回答

5

根据您的代码片段,您需要执行以下操作:

var user = Map("name" -> "1", "id" -> 1)
var json: JsValue = Json.obj(
    "user" -> Json.toJson(user)
)

下一个可能出现的问题是Play找不到类型为Map[String, Any]的隐式写入(writes)。我建议为您尝试序列化的对象编写自己的写入(Writes)程序。也许可以像下面这样写:
case class User(props: Map[String, Any])

implicit val writes: Writes[User] = new Writes[User]{
  override def writes(o: User): JsValue = {
    val propsJs = JsObject(
      o.props.map{kvp =>
        kvp._1 -> (kvp._2 match {
          case x: String => JsString(x)
          case x: Int => JsNumber(x)
          case _ => JsNull // Do whatever you want here.
        })
      }
    )
    JsObject(Map("user" -> propsJs))
  }
}

使用测试:

val u1 = User(Map("name" -> "1", "id" -> 1))

val js = Json.toJson(u1)

out: js: play.api.libs.json.JsValue = {"user":{"name":"1","id":1}}

编辑:

正如评论中指出的那样,根据您的使用情况,执行以下操作可能是有利的:

case class User(name: String, id: Int)

implicit val writes = Json.writes[User]

这是一个更易读且更加明确定义User的方法。如果可能的话,我们建议您采用此方法(以及其他人)。


2
我认为使用case class User(props: Map[String, Any])会让人感到困惑。为什么不指定User的实际成员字段,如StringInt等,然后根据http://stackoverflow.com/questions/21714825/create-writes-and-format-of-case-class/定义一个`Writes[User]`呢? - Kevin Meredith
@KevinMeredith 我同意你的观点。OP要求序列化Map[String, Any]。我会按照上述建议进行更新。 - nattyddubbs
1
谢谢。此外,我建议阅读Travis Brown的这篇有用的解释 - https://github.com/travisbrown/circe/issues/216#issuecomment-191730128。 - Kevin Meredith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接