Scala读取:如何处理选项[Map [String,Any]]

时间:2014-09-09 09:30:39

标签: json scala

我无法理解为什么它会在以下行引发错误:

val anyReads = Reads[Any](m => metaValueToJsValue(m))

错误消息:类型不匹配;发现:play.api.libs.json.JsValue required:play.api.libs.json.JsResult [Any]注意:隐含值readsMap在这里不适用,因为它位于应用程序点之后且缺少显式结果类型

我在下面粘贴了我的代码。 任何帮助表示赞赏!谢谢!

import play.api.libs.json._
import play.api.libs.json.util._
import play.api.libs.functional.syntax._

case class TempClass(
  metaValue: Option[Map[String, Any]])

object TempClass {

  val anyReads = Reads[Any](m => metaValueToJsValue(m))

  def metaValueToJsValue(m: Any): JsValue = {
    m match {
      case s: String => JsString(s)
      case n: Int => JsNumber(n): JsValue
      case n: Long => JsNumber(n): JsValue
      case n: Double => JsNumber(n): JsValue
      case n: BigDecimal => JsNumber(n): JsValue
      case b: Boolean => JsBoolean(b)
      case l: Seq[Any] => JsArray(l.map(metaValueToJsValue)): JsValue
    }
  }

  implicit val readsMap = Reads[Map[String, Any]](m => Reads.mapReads[Any](anyReads).reads(m))
  implicit val reads = Json.reads[TempClass]
}

2 个答案:

答案 0 :(得分:2)

看起来你在这里做错了什么。

  

val anyReads =读取[任意](m => metaValueToJsValue(m))

Reads将一个接受JsValue并返回JsResult[A]的函数作为参数,但是你传递的函数接受Any并返回JsValue。

所以def metaValueToJsValue(m: Any): JsValue = {的签名应该是这样的

def metaValueToJsValue(m: JsValue): JsResult[Any] = {

注意:通常框架不会为多态映射(Map [String,Any])提供默认序列化器,因为Any可以再次为Maps / Lists并且可以转到任何级别。因此,应用程序应该了解传入的JSON结构。在下面的代码中我假设了一个级别,即Json属性值本身不能是Json对象/数组

请参阅以下代码:

object TempClass {

    implicit val readsMap = Reads[Map[String, Any]](m => Reads.mapReads[Any](anyReads).reads(m))
    implicit val reads = Json.reads[TempClass]
    val anyReads = Reads[Any](m => metaValueToJsValue(m))

    def metaValueToJsValue(m: JsValue): JsResult[Any] = {
      m match {
        case JsObject(m) => {
          val m1 = m.map(f => (f._1, convert(f._2))).toMap
          JsSuccess(m1)
        }
        case JsString(s) => JsSuccess(s)
        case JsNumber(n) => JsSuccess(n)
        case JsBoolean(b) => JsSuccess(b)
        case JsArray(arr) => {
          val list = arr.map(convert)
          JsSuccess(list)
        }
      }
    }

    def convert(m: JsValue): Any = {
      m match {
        case JsString(s) => s
        case JsNumber(n) => n
        case JsBoolean(b) => b
      }
    }

    def main(args: Array[String]) {
      val json = """{"metaValue" : {"name":"John", "age":30}}"""
      val tmpClass = Json.parse(json).as[TempClass]
      println(tmpClass)
    }

  }

答案 1 :(得分:0)

我发现将JsObject放在最后会更好,否则一切都可以作为JsObject进行匹配。此外,还不需要转换。更好的解决方案(虽然不完整)如下:

object ContractDetails {
  implicit val readsMap = Reads[Map[String, Any]](m => Reads.mapReads[Any](anyReads).reads(m))
  implicit val reads = Json.reads[ContractDetails]
  val anyReads = Reads[Any](m => metaValueToJsValue(m))

  def metaValueToJsValue(m: JsValue): JsResult[Any] = {
    m match {
      case JsBoolean(b) => JsSuccess(b)
      case JsNumber(n) => JsSuccess(n)
      case JsString(s) => JsSuccess(s)
      case JsArray(arr) => {
        val list = arr.map(metaValueToJsValue)
        JsSuccess(list)
      }
      case JsNull => JsSuccess(null)
      //case x => JsFailure(x.toString())
      case JsObject(m) => {
        val m1 = m.map(f => (f._1, metaValueToJsValue(f._2))).toMap
        JsSuccess(m1)
      }
    }
  }