【问题标题】:How to parse a JSON Scala without case classes如何在没有案例类的情况下解析 JSON Scala
【发布时间】:2016-08-11 23:12:35
【问题描述】:

我有一个可以随时间变化的 JSON,使用 case Class 可能不方便,因为每次 JSON 更改时我都需要更改它的结构。

例如,如果我有这样的 JSON:

val json= """{
  "accounts": [
  { "emailAccount": {
    "accountName": "YMail",
    "username": "USERNAME",
    "password": "PASSWORD",
    "url": "imap.yahoo.com",
    "minutesBetweenChecks": 1,
    "usersOfInterest": ["barney", "betty", "wilma"]
  }},
  { "emailAccount": {
    "accountName": "Gmail",
    "username": "USER",
    "password": "PASS",
    "url": "imap.gmail.com",
    "minutesBetweenChecks": 1,
    "usersOfInterest": ["pebbles", "bam-bam"]
  }}
  ]
}"""

我可以通过以下方式访问它吗:

val parsedJSON = parse(json)
parsedJSON.accounts(0).emailAccount.accountName

【问题讨论】:

    标签: json scala parsing lift-json circe


    【解决方案1】:

    circe 的光学模块几乎完全支持您要求的语法:

    import io.circe.optics.JsonPath.root
    
    val accountName = root.accounts.at(0).emailAccount.accountName.as[String]
    

    然后,如果你有这个 JSON 值(我正在使用 circe 的 JSON 文字支持,但你也可以使用 io.circe.jawn.parseparse 函数)解析字符串以获得 Json 值合作):

    import io.circe.Json, io.circe.literal._
    
    val json: Json = json"""{
      "accounts": [
      { "emailAccount": {
        "accountName": "YMail",
        "username": "USERNAME",
        "password": "PASSWORD",
        "url": "imap.yahoo.com",
        "minutesBetweenChecks": 1,
        "usersOfInterest": ["barney", "betty", "wilma"]
      }},
      { "emailAccount": {
        "accountName": "Gmail",
        "username": "USER",
        "password": "PASS",
        "url": "imap.gmail.com",
        "minutesBetweenChecks": 1,
        "usersOfInterest": ["pebbles", "bam-bam"]
      }}
      ]
    }"""
    

    您可以尝试像这样访问帐户名称:

    scala> accountName.getOption(json)
    res0: Option[String] = Some(YMail)
    

    由于 circe-optics 是基于 Monocle 构建的,因此您可以获得其他一些不错的功能,例如不可变更新:

    scala> accountName.modify(_.toLowerCase)(json)
    res2: io.circe.Json =
    {
      "accounts" : [
        {
          "emailAccount" : {
            "accountName" : "ymail",
            ...
    

    等等。


    更新:circe 设计为模块化,因此您只需“支付”您需要的部分。上面的示例需要类似于以下 SBT 设置:

    scalaVersion := "2.11.8"
    
    val circeVersion = "0.4.1"
    
    libraryDependencies ++= Seq(
      "io.circe" %% "circe-core" % circeVersion,
      "io.circe" %% "circe-jawn" % circeVersion,
      "io.circe" %% "circe-literal" % circeVersion,
      "io.circe" %% "circe-optics" % circeVersion
    )
    

    …或者对于 Maven:

    <dependency>
      <groupId>io.circe</groupId>
      <artifactId>circe-core_2.11</artifactId>
      <version>0.4.1</version>
    </dependency>
    <dependency>
      <groupId>io.circe</groupId>
      <artifactId>circe-jawn_2.11</artifactId>
      <version>0.4.1</version>
    </dependency>
    <dependency>
      <groupId>io.circe</groupId>
      <artifactId>circe-literal_2.11</artifactId>
      <version>0.4.1</version>
    </dependency>
    <dependency>
      <groupId>io.circe</groupId>
      <artifactId>circe-optics_2.11</artifactId>
      <version>0.4.1</version>
    </dependency>
    

    【讨论】:

    • 感谢您的回复。但是,尽管您的解决方案看起来很有希望,但它不起作用。 error: not found: value optics 估计还没准备好?然后你的解释变得很混乱......尤其是前两行代码。如何访问 JSON 的元素?谢谢
    • @salvob 查看我关于依赖项的更新。不确定“访问元素”是什么意思?您想直接使用 JSON AST?
    • 现在我明白你的意思了,它看起来很“神奇”。我发现在导入真正的 json 之前必须定义 AST 的“路径”很奇怪。 this -> val accountName = root.accounts.at(0).emailAccount.accountName.as[String] 必须被定义,然后用getOption 获取字段,但它看起来是可行的。谢谢!
    • @salvob 对——镜头使路径成为一流的对象,就像函数式编程使函数成为一流的一样。
    • 我添加了 maven 依赖项以防其他人需要,还添加了到 JawnParser 的链接,以便全面了解 jawn 模块的工作原理。希望这会有所帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-23
    • 2019-11-16
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多