【问题标题】:How to convert a map to json where one of the types is AnyRef如何将地图转换为其中一种类型为 AnyRef 的 json
【发布时间】:2020-01-03 17:48:32
【问题描述】:

我有一个类型为[String, AnyRef] 的地图。如何使用 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?

修正 =>
这张地图是弹性搜索的结果,所以我不能保证每次地图都是一样的,但它会是同一种类型,即 Map[String, AnyRef]。

【问题讨论】:

    标签: json scala elasticsearch play-json


    【解决方案1】:

    Play-json 尽量保证类型安全,这意味着它不能为Map[String, AnyRef] 提供隐式序列化程序。它可能会影响其他序列化程序,并且无法确保它不会失败。你可以创建自己的,但它是硬编码的,你不应该把它放在你的全局变量中:

    // Start writing your ScalaFiddle code here
    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) 
      )
    )
    
    //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 => v
      case v:Map[String,_] => JsObject(v.map { case (k,v) => (k, toJsValue(v)) }) //keep in mind we assume this is map of [String,Any] if not it will blow up :(
      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))
    
    //here we use anyWriter implicitly but there is other one... currently shadowed :(.
    println(Json.toJson(testMap.mapValues[JsValue] { case (v) => toJsValue(v) }))
    

    但请记住,当您在 Map 中使用不受支持的类型时,它会爆炸。我想最好的方法不是创建隐式编写器,而是在映射的参数上使用toJsValue 方法,然后尝试对其进行序列化(如最后一个println)。

    demo

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

    【讨论】:

    • 感谢您的回答。我正在尝试从弹性搜索中获取结果,一旦搜索失败,它将不会返回地图内元素的确切数量。对不起,我忘了在我的问题中添加这个
    • 我猜没关系。更重要的是支持所有可能的类型。
    【解决方案2】:

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

    val stringMap = testMap.mapValues(_.toString)
    

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

    【讨论】:

      【解决方案3】:

      你不能这样做。

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

      或者换句话说,您需要一个类似JSON 的对象结构,其中您有Writers,分别。 读者

      如果您需要进一步解释,请告诉我。

      【讨论】:

        猜你喜欢
        • 2021-10-24
        • 1970-01-01
        • 2012-09-17
        • 2012-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-07
        • 2021-01-11
        相关资源
        最近更新 更多