使用 play framework 和 scala 检查或查找 json 中是否存在特定路径

Check or find if a particular path exists in json using play framework and scala

我正在尝试使用 play 框架将 json 解析为案例 class。我的目的是检查 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())))))

是否有一个预定义的 function/method 正在播放,以便我可以检查这个

(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.

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

可能会有帮助...

第一种方法是使用 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`)
}

改变publicAPI.

第二种方法是使用 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,可能。

对不起我的英语。

没有 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
}

然后reading/parsing 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"}}