【问题标题】:Parse json list to two list types by field value in Scala circe在Scala circe中按字段值将json列表解析为两种列表类型
【发布时间】:2018-02-14 06:00:59
【问题描述】:

我得到了(示例)json:

{
    "version": 1.1,
    "author": "XYZ",
    "elements": [{
            "type": "nodeX",
            "id": 1,
            "a": 1,
            "b": 2
        },
        {
            "type": "nodeX",
            "id": 2,
            "a": 1,
            "b": 2
        },

        ...

        {
            "type": "nodeX",
            "id": 13214,
            "a": 1,
            "b": 2
        },
        {
            "type": "nodeY",
            "id": 1,
            "c": [
                "qaz",
                "wsx"
            ]
        },
        {
            "type": "nodeY",
            "id": 2,
            "c": [
                "qaz",
                "wsx"
            ]
        },

        ...

        {
            "type": "nodeY",
            "id": 3,
            "c": [
                "qaz",
                "wsx"
            ]
        }
    ]
}

元素列表总是包含有两种可能性的对象:

  • 键入“nodeX”和属性:idab
  • 键入“nodeY”和属性:idc

我想获取给定类的两个列表:

case class NodeX(val id:Long, val a:Long, val b:Long)
case class NodeY(val id:Long, val c:List[String])

我已尝试使用 circe(Scala 库)将此 json 解析为类:

case class Element(val 'type':String, val id:Long, val a:Option[Long],val b:Option[Long], val c:Option[List[String]])
case class MyJson(val version:Double, val author:String, val elements:List[Element])

但不幸的是,我得到了带有可选字段的对象元素列表。
目前我正在使用它作为解决方法:

val elements = // MyJson.elements
for (elem <- elements)
    elem match {
      case Element("nodeX", _,_,_,_) => //here convert to NodeX and add to list List[NodeX]
      case Element("nodeY", _,_,_,_) => //here convert to NodeY and add to list List[NodeY]
    }

我正在寻找更好的解决方案,更快的解决方案,因为此 json 中的列表包含不少于 70k 的元素。

提前致谢:)

【问题讨论】:

  • 你是如何解码的?半自动派生解码器?
  • @michaJlS 我用decode[MyJson](json) 解码,我对circe 没有经验。我阅读了有关半自动派生解码器的信息,但我不知道如何使用
  • 你必须展示你的解析代码并告诉你从这个 json 中获取的位置。

标签: json scala list parsing circe


【解决方案1】:

如果允许您将NodeXNodeY 类定义为具有密封特征的ADT,那么jsoniter-scala 可以轻松解析它。

将库添加到您的依赖项列表中

libraryDependencies ++= Seq(
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "0.29.2" % Compile, 
  "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "0.29.2" % Provided // required only in compile-time
)

定义你的案例类:

sealed trait Node
final case class NodeX(val id:Long, val a:Long, val b:Long) extends Node
final case class NodeY(val id:Long, val c:List[String]) extends Node

case class MyJson(val version:Double, val author:String, val elements:List[Node])

为根案例类生成编解码器并使用它

import java.io._
import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._

val myJsonCodec = JsonCodecMaker.make[MyJson](CodecMakerConfig())

val myJson = {
  val fin = new FileInputStream("/tmp/my.json")
  try readFromStream(codec, fin)
  finally fin.close()
}

【讨论】:

    【解决方案2】:

    另一种方式是 dynamic Scala in Circe + Monocle 为您提供不安全的灵活性动态语言:

    libraryDependencies += "io.circe" %% "circe-optics" % circeVersion
    import io.circe.optics.JsonPath._
    import io.circe.parser
    
    val json = parser.parse(jsonString).right.get
    
    case class NodeX(val id:Long, val a:Long, val b:Long)
    case class NodeY(val id:Long, val c:List[String]) 
    
    val nodexs = root.elements.each
        .filter(root.`type`.string.getOption(_).contains("nodeX"))
        .as[NodeX].getAll(json)
    //res: List[NodeX] = List(NodeX(1L, 1L, 2L), NodeX(2L, 1L, 2L))
    val nodeys = root.elements.each
        .filter(root.`type`.string.getOption(_).contains("nodeY"))
        .as[NodeY].getAll(json)
    //res: List[NodeY] = List(NodeY(3L, List("qaz", "wsx")))
    

    而且我相信 circe-generic auto mode 可以做到与 Andriy Plokhotnyuk 的回答中描述的相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-06
      • 1970-01-01
      • 2019-05-31
      • 1970-01-01
      • 2016-10-15
      • 1970-01-01
      • 2019-03-28
      • 1970-01-01
      相关资源
      最近更新 更多