如何将一个包含AnyRef类型的地图转换为JSON。

huangapple go评论73阅读模式
英文:

How to convert a map to json where one of the types is AnyRef

问题

我有一个类型为[String, AnyRef]的Map。如何使用play-json将其转换为Json?我找到了一些答案,但它们都定义了一个类型。当我尝试进行转换时,我得到了编译器错误消息:

> 没有隐式参数的类型:Writes[Map[String, AnyRef]]

val testMap: Map[String, AnyRef] = Map.empty[String, AnyRef]

假设此地图具有以下数据:

{ "lastModifiedTime" -> 1575311400000,
  "user_id" -> 1,
  "user_email" -> "user@email.com",
  "is_active" -> true,
  "project_id" -> 1,
  "status" -> "COMPLETED" }

所以我的问题是,如果我在地图中将AnyRef作为第二类型,如何只使用play-Json将其转换为Json?

更正 =>
此地图是作为Elasticsearch结果的一部分获得的,因此我不能保证每次地图都相同,但它们将是相同类型的地图,即Map[String, AnyRef]。

英文:

I have a Map which is of the type [String, AnyRef]. How do i convert it to Json using play-json. I found some answers but they all had a type defined. When i tried converting i got the compiler error message as:

> No implicit arguments of type: Writes[Map[String, AnyRef]]

val testMap: Map[String, AnyRef] = Map.empty[String, AnyRef]

Suppose this map has the following data:

{ "lastModifiedTime" -> 1575311400000,
    "user_id" -> 1,
    "user_email" -> "user@email.com",
    "is_active" -> true,
    "project_id" -> 1,
    "status"-> "COMPLETED" }

So my question is if I have AnyRef as the second type in map how will I convert it to Json using just play-Json ?

Correction => <br/>
This map is obtained as a result of elastic search result, so i cannot guarantee that every time the map will be same, but it will be of the same type that is, Map[String, AnyRef].

答案1

得分: 3

Play-json试图尽可能类型安全,这意味着它无法为Map[String, AnyRef]提供隐式序列化器。这可能会遮盖其他序列化器,无法确保它不会失败。您可以创建自己的序列化器,但它将是硬编码的,您不应该将其放在全局范围内:

// 在这里开始编写你的ScalaFiddle代码
import play.api.libs.json._

val testMap = Map[String, Any](
  "test" -> 1, 
  "otherTest" -> "denada",
  "mapsAlsoWorksSomehow" -> Map (
    "wee" -> true,
    "mapsEverywhere" -> Map (
       "wee" -> false, "prr" -> new java.util.Date(),
    ),
    "ops" -> new java.lang.Integer(22) 
  )
)

// 你可以创建一个函数,只需将对象映射到JsValues,而不使用类型...
// 不安全,但有效
def toJsValue(a: Any): JsValue = a match {
  case v: JsValue => v
  case v: Map[String, _] => JsObject(v.map { case (k, v) => (k, toJsValue(v)) }) //请记住,我们假设这是[String,Any]的映射,否则它会失败 :(
  case v: String => JsString(v)
  case v: Int => JsNumber(v)
  case v: Boolean => JsBoolean(v)
  case v: java.util.Date => JsString(v.toString)
  case v if v == null => JsNull
}

implicit lazy val anyWriter: Writes[Map[String, Any]] = new Writes[Map[String, Any]] {
  def writes(a: Map[String, Any]): JsValue = toJsValue(a)
}

println(Json.toJson(testMap))
println(toJsValue(testMap))

// 这里我们隐式使用了anyWriter,但还有其他的... 目前被遮盖了 :(。
println(Json.toJson(testMap.mapValues[JsValue] { case (v) => toJsValue(v) }))

但请记住,当您在Map中有不受支持的类型时,它会失败。我猜最好的方法是不创建隐式写入器,而是在Map的参数上使用toJsValue方法,然后尝试序列化它(就像最后的println中一样)。

演示

如果这有帮助,请投票并标记为答案。

英文:

Play-json try to be as typesafe as possible and this means it cannot provide implicitly serializer for Map[String, AnyRef]. It could shadow other serializers and it couldn't ensure it will not fail. You can create your own but it'll be hard-coded and you should not put it in your globals:

// Start writing your ScalaFiddle code here
import play.api.libs.json._

val testMap = Map[String,Any](
  &quot;test&quot; -&gt; 1, 
  &quot;otherTest&quot; -&gt; &quot;denada&quot;,
  &quot;mapsAlsoWorksSomehow&quot; -&gt; Map (
    &quot;wee&quot; -&gt; true,
    &quot;mapsEverywhere&quot; -&gt; Map (
       &quot;wee&quot; -&gt; false, &quot;prr&quot; -&gt; new java.util.Date(),
    ),
    &quot;ops&quot; -&gt; new java.lang.Integer(22) 
  )
)

//you can create your own function that just maps objects to JsValues without using types...
//not safe but works
def toJsValue(a:Any):JsValue = a match {
  case v:JsValue =&gt; v
  case v:Map[String,_] =&gt; JsObject(v.map { case (k,v) =&gt; (k, toJsValue(v)) }) //keep in mind we assume this is map of [String,Any] if not it will blow up :(
  case v:String =&gt; JsString(v)
  case v:Int =&gt; JsNumber(v)
  case v:Boolean =&gt; JsBoolean(v)
  case v:java.util.Date =&gt; JsString(v.toString)
  case v if v == null =&gt; JsNull
}

implicit lazy val anyWriter:Writes[Map[String,Any]] = new Writes[Map[String,Any]] {
  def writes(a:Map[String,Any]):JsValue = toJsValue(a)
}


println(Json.toJson(testMap))
println(toJsValue(testMap))

//here we use anyWriter implicitly but there is other one... currently shadowed :(.
println(Json.toJson(testMap.mapValues[JsValue] { case (v) =&gt; toJsValue(v) }))

But keep in mind that it will blow up when You will have unsupported type in Map. I guess best way is not to create implicit writer but use toJsValue method on arguments of a map and then try to serialize it (like in last println).

demo

If it was helpful, up-vote please and mark as answer.

答案2

得分: 1

最简单的选项是将所有的值转换为 String 并保存。

val stringMap = testMap.mapValues(_.toString)

如果无法实现这个,您要么必须为 AnyRef 创建一个自定义处理程序,要么手动从 Map 创建一个 JObject,并使用 match 来选择每个元素的特定 JSON 类型。

英文:

The simplest option is to convert all the values to String and save that.

val stringMap = testMap.mapValues(_.toString)

Failing that you either have to create a custom handler for AnyRef or manually create a JObject from the Map and use match to select the specific JSON type for each element.

答案3

得分: 0

你不能这样做。

你可以拥有的最接近 AnyRef 的东西是 play.api.libs.json.JsValue

换句话说,你需要一个类似于 JSON 的对象结构,其中包含 WritersReaders

如果需要更多解释,请告诉我。

英文:

You cannot do that.

The closest thing to AnyRef you can have is play.api.libs.json.JsValue.

Or in other words you need a JSON-like object structure, where you have Writers, resp. Readers.

Let me know if you need further explanation.

huangapple
  • 本文由 发表于 2020年1月3日 15:04:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/59574474.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定