【问题标题】:Check or find if a particular path exists in json using play framework and scala使用 play framework 和 scala 检查或查找 json 中是否存在特定路径
【发布时间】:2019-12-06 21:30:10
【问题描述】:

我正在尝试将使用播放框架的 json 解析为案例类。我的目的是检查 json 中是否存在特定路径。如果存在,则仅读取该路径处的元素。

这是我的代码

package com.learning.avinash.query

import play.api.libs.json.{JsDefined, JsPath, Json, Reads, Writes, __}
import play.api.libs.functional.syntax._

object ParseJson extends App {

  case class Msisdn(primaryId: Option[String], relatedAccountId: Option[String])

  object Msisdn {
    implicit val readData: Reads[Msisdn] = (
      (JsPath \ "primary-id").readNullable[String] ~
      (JsPath \ "meta" \ "related-account-id").readNullable[String]
      ) (Msisdn.apply _)
  }



val testJson = """{
  "resources": [
    {
    "valid-for-start-datetime": "2019-08-23T10:47:17.485Z",
    "primary-id": "393823468684",
      "meta": {
        "related-account-id": "10001771",
            "roles": [
              "customer"
            ]
        }
    },
  {
    "valid-for-start-datetime": "2019-08-23T10:47:17.485Z",
    "primary-id": "393823467689"
    }
  ]
}"""
    println((Json.parse(testJson) \ "resources").as[List[Msisdn]])
}

在第二个对象的资源数组中,你可以观察到这段代码完全丢失了

"meta": {
            "related-account-id": "10001771",
                "roles": [
                  "customer"
                ]
            }

现在当我尝试解析 json 时,它失败了,我得到了以下异常

Exception in thread "main" play.api.libs.json.JsResultException: JsResultException(errors:List(((1)/meta/related-accoount-id,List(JsonValidationError(List(error.path.missing),WrappedArray())))))

是否有一个预定义的函数/方法在起作用,以便我可以检查是否这个

(JsPath \ "meta")

存在特定路径,然后仅读取该路径中的元素。 或者我应该编写一个自定义函数来检查路径是否存在。

我可以看到一个需要 Jsvalue 的 JsDefined()。

https://www.playframework.com/documentation/2.7.x/api/scala/play/api/libs/json/index.html

final case classJsDefined(value: JsValue)
Wrapper for JsValue to represent an existing Json value.

请有任何想法、想法、帮助或建议。

【问题讨论】:

  • accoount (related-accoount-id) 中存在语法错误。当然,这不是例外的原因

标签: json scala playframework


【解决方案1】:

也许会有所帮助...

第一种方式是使用Json.format:

import play.api.libs.json._

object ParseJson extends App {

  case class Msisdn(`primary-id`: Option[String], meta: Option[Meta])
  case class Meta(`related-account-id`: Option[String])
  implicit val metaFormat = Json.format[Meta]
  implicit val msisdnFormat = Json.format[Msisdn]

输出: List(Msisdn(Some(393823468684),Some(Meta(Some(10001771)))), Msisdn(Some(393823467689),None))

然后就可以使用了

case class Msisdn(private val `primary-id`: Option[String],
                  private val meta: Option[Meta]) {
  def primaryId: Option[String] = `primary-id`
  def accountId: Option[String] = meta.flatMap(_.`related-account-id`)
}

更改公共 API。

第二种方法是使用recursive path。但我认为你可以谨慎使用它。

  case class Msisdn(primaryId: Option[String], relatedAccountId: Option[String])
  object Msisdn {
    implicit val readData: Reads[Msisdn] = (
      (JsPath \ "primary-id").readNullable[String] ~
        (JsPath \\ "related-account-id").readNullable[String]
      ) (Msisdn.apply _)
  }

第三个是使用new Format。在这种情况下,您可以使用 json 对象,并检查该字段是否存在。

还有另一种方法,但我忘了怎么做。大概是用fmap吧。

对不起我的英语。

【讨论】:

  • 案例类字段没有必要不尊重通常的命名,因为它与 JSON 不同。
  • @Mikhail Ionkin:使用递归路径的 2 种方式不起作用。即使 json 中存在相关帐户 ID 值,它也会返回 List(Msisdn(Some(393823468684),None), Msisdn(Some(393823467689),None))
  • @AvinashReddy 这是因为accoount 中有语法错误。我复制它。现在我修复答案
  • @cchantep 它会产生哪些问题,如果我要隐藏公共 API?我认为它的好处涵盖了缺点
  • 别这么认为,JSON就是JSON,Scala就是Scala
【解决方案2】:

没有flatten annotation 可用,但按规则玩会更容易。

import play.api.libs.json._

case class MsisdnMeta(relatedAccountId: Option[String])

object MsisdnMeta {
  private implicit val jsonConfiguration: JsonConfiguration =
    JsonConfiguration(JsonNaming { _ => "related-account-id" })

  implicit val format: OFormat[MsisdnMeta] = Json.format
}

case class Msisdn(
  primaryId: Option[String],
  meta: Option[MsisdnMeta]) {

  @inline def relatedAccountId: Option[String] =
    meta.flatMap(_.relatedAccountId)
}

object Msisdn {
  private implicit val jsonConfiguration: JsonConfiguration =
    JsonConfiguration(JsonNaming {
      case "primaryId" => "primary-id"
      case _ => "meta"
    })

  implicit val format: OFormat[Msisdn] = Json.format
}

然后读取/解析 JSON:

val json = Json.parse("""{
  "valid-for-start-datetime": "2019-08-23T10:47:17.485Z",
  "primary-id": "393823468684",
  "meta": {
    "related-account-id": "10001771",
    "roles": [
      "customer"
    ]
  }
}""")

val res = json.validate[Msisdn]
// play.api.libs.json.JsResult[Msisdn] = JsSuccess(Msisdn(Some(393823468684),Some(MsisdnMeta(Some(10001771)))),)

res.foreach { suc =>
  println(s"relatedAccountId = ${suc.relatedAccountId}")
}
// relatedAccountId = Some(10001771)

... 并写入 JSON:

Json.toJson(Msisdn(Some("id"), Some(MsisdnMeta(Some("bar")))))
// play.api.libs.json.JsValue = {"primary-id":"id","meta":{"related-account-id":"bar"}}

使用嵌套元数据创建 Msisdn 可以更轻松地向伴随对象添加一些工厂。

object Msisdn {
  // Added factory
  @inline def apply(
    primaryId: Option[String],
    relatedAccountId: String): Msisdn =
    Msisdn(primaryId, Some(MsisdnMeta(Some(relatedAccountId))))

  private implicit val jsonConfiguration: JsonConfiguration =
    JsonConfiguration(JsonNaming {
      case "primaryId" => "primary-id"
      case _ => "meta"
    })

  implicit val format: OFormat[Msisdn] = Json.format
}

Json.toJson(Msisdn(Some("id"), "bar"))
// play.api.libs.json.JsValue = {"primary-id":"id","meta":{"related-account-id":"bar"}}

【讨论】:

    猜你喜欢
    • 2014-02-06
    • 2016-09-05
    • 2019-10-04
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 2021-02-17
    • 2019-04-03
    相关资源
    最近更新 更多