【问题标题】:Dynamic type casting for JsValue field in Scala PlayScala Play 中 JsValue 字段的动态类型转换
【发布时间】:2017-11-13 05:17:05
【问题描述】:

由于我正在编写一个函数来在我的 Scala 代码中从另一个 API 请求数据,因此响应 Json 的格式如下:

"data": {
      "attributeName": "some String",
      "attributeValue": false,
      "attributeSource": "Manual",
      "attributeValueLabel": null
  },
 "data": {
      "attributeName": "some String",
      "attributeValue": "daily",
      "attributeSource": "Manual",
      "attributeValueLabel": "Almost Daily"
  }

请注意,有时attributeValue 的类型是String 值,有时它是Boolean 值。

所以我正在尝试编写自己的读取和写入来动态读取类型。

case class Data(attributeName: Option[String], attributeValue: Option[String], attributeSource: Option[String], attributeValueLabel: Option[String])

object Data{

  implicit val readsData: Reads[Data] = {
    new Reads[Data] {
      def reads(json: JsValue) = {
        val attrValue = (json \ "attributeValue").as[] // How to cast to Boolean some time, but some other time is a String here
        ......
      }
    }
  }

正如您在我的评论中看到的那样,我坚持将(json \ "attributeValue") 转换为String/Boolean,基于API 的返回类型。我该怎么做?

【问题讨论】:

    标签: json scala playframework casting


    【解决方案1】:

    您可以尝试先将其解析为String,然后再解析为Boolean

    val strO = (json \ "attributeValue").asOpt[String]
    val value: Option[String] = strO match {
      case str@Some(_) => str
      case None        => (json \ "attributeValue").asOpt[Boolean].map(_.toString)
    }
    

    【讨论】:

    • 嗨,感谢您的回答,但这种方式不起作用,它在解析为字符串的步骤中失败,因为实际类型是布尔型
    • 已修复。你需要先检查一个或另一个
    【解决方案2】:

    当您尝试以不同方式读取属性时,可以使用.orElse 函数:

    import play.api.libs.json.{JsPath, Json, Reads}
    import play.api.libs.functional.syntax._
    
    
    val json1 =
     """
       |{
       |  "attributeName": "some String",
       |  "attributeValue": false
       |}
     """.stripMargin
    
    val json2 =
      """
        |{
        |  "attributeName": "some String",
        |  "attributeValue": "daily"
        |}
      """.stripMargin
    
    // I modified you case class to make the example short
    case class Data(attributeName: String, attributeValue: String)
    object Data {
    
      // No need to define a reads function, just assign the value
      implicit val readsData: Reads[Data] = (
        (JsPath \ "attributeName").read[String] and
    
        // Try to read String, then fallback to Boolean (which maps into String)
        (JsPath \ "attributeValue").read[String].orElse((JsPath \ "attributeValue").read[Boolean].map(_.toString))
      )(Data.apply _)
    }
    
    println(Json.parse(json1).as[Data])
    println(Json.parse(json2).as[Data])
    

    输出:

    Data(some String,false)
    Data(some String,daily)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多