【问题标题】:Scala - handling JSON with nested structures of maps and listsScala - 使用地图和列表的嵌套结构处理 JSON
【发布时间】:2018-04-11 15:24:07
【问题描述】:

Api 请求 返回一个 Json 字符串,如下所示。我一直在尝试在 Scala 中解析,将 "name" 字段添加到列表中,然后使用另一个列表过滤此列表。 API 来自第三方,因此无法修改格式。

示例列表(此处的值中有 2 个条目,但最多可以有 300 个):

{
    "size": 20,
    "values": [{
            "name": "mullock",
            "upstatus": "Green",
            "details": {
                "key": "rupture farms",
                "server": "mudos",
                "owner": "magog_cartel",
                "type": "NORMAL",
                "links": {
                    "self": [{
                        "address": "https://mudos.com:port/access"
                    }]
                }
            }
        },
        {
            "name": "tassadar",
            "upstatus": "Orange",
            "details": {
                "key": "archon",
                "server": "protoss",
                "owner": "aspp67",
                "type": "NORMAL",
                "links": {
                    "self": [{
                        "address": "https://aiur.com:port/access"
                    }]
                }
            }
        }
    ],
    "limit": 100
}

我尝试使用 jackson 和一些在应用程序的其他部分中使用的建议函数(如下)来解组字符串,但我不完全理解这一点。

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

object Json {
    /* Json/Scala translation utilities
     */
    val mapper = {
        val _mapper = new ObjectMapper() with ScalaObjectMapper
        _mapper.registerModule(DefaultScalaModule)
        _mapper
    }

    def dump(obj: Any): String = {
        mapper.writeValueAsString(obj)
    }

    def dumpObj(fields: (Any, Any)*): String = {
        dump(fields.toMap)
    }

    def read[T: Manifest](content: String): T = {
        mapper.readValue(content, mapper.constructType[T])
    }

    def readObj(content: String): Map[Any, Any] = {
        return read[Map[Any, Any]](content)
    }

}
  1. 如何访问对象 Map(Any, Any) 中的嵌套元素?
  2. 这是反序列化 JSON 字符串的正确方法吗?

非常感谢任何帮助!

【问题讨论】:

  • 目前这个问题非常接近“帮助我调试我的代码”和“推荐一个工具”之类的问题——而且这两个问题在 StackOverflow 上都被认为是题外话(通常是关闭的)。话虽如此,您可能想看看circesprayplay-json
  • 我在问如何访问数组类型 ANY 中的嵌套元素,或者如何从输入 Json 字符串中正确展平嵌套混合类型数组
  • 请更新答案。但想法是你不能 - Any 可以是任何类型,包括字符串、int、float、boolean 或自定义对象。您需要将其反序列化为某个特定的类才能访问嵌套结构。我在评论中概述的所有三个库都允许这样做。

标签: json scala unmarshalling


【解决方案1】:

在 Scala 中,您应该始终更喜欢有用的类型而不是 Any。不要将 JSON 解析为 Map[String, Any] - 围绕 Api Result 数据结构设计案例类并将 JSON 读入该类:

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper

case class ApiResult(size: Int, limit: Int, values: Seq[Entity])
case class Entity(name: String, upstatus: String, details: EntityDetails)
case class EntityDetails(key: String, server: String, owner: String, `type`: String, links: EntityLinks)
case class EntityLinks(self: Seq[EntityLinkAddress])
case class EntityLinkAddress(address: String)

object Foo {
  val mapper = new ObjectMapper() with ScalaObjectMapper
  mapper.registerModule(DefaultScalaModule)

  def test(): Unit = {
    val json: String =
      """
        |{
        |    "size": 20,
        |    "values": [{
        |            "name": "mullock",
        |            "upstatus": "Green",
        |            "details": {
        |                "key": "rupture farms",
        |                "server": "mudos",
        |                "owner": "magog_cartel",
        |                "type": "NORMAL",
        |                "links": {
        |                    "self": [{
        |                        "address": "https://mudos.com:port/access"
        |                    }]
        |                }
        |            }
        |        },
        |        {
        |            "name": "tassadar",
        |            "upstatus": "Orange",
        |            "details": {
        |                "key": "archon",
        |                "server": "protoss",
        |                "owner": "aspp67",
        |                "type": "NORMAL",
        |                "links": {
        |                    "self": [{
        |                        "address": "https://aiur.com:port/access"
        |                    }]
        |                }
        |            }
        |        }
        |    ],
        |    "limit": 100
        |}
        |""".stripMargin

    val r = mapper.readValue[ApiResult](json)

    println(r.values.find(_.name == "tassadar"))
  }
}

【讨论】:

  • 所以一般没有办法定义反序列化方法来解压JSon字符串,我必须明确定义结构?另外为什么在这里使用案例类来定义结构而不是常规类?
  • 您可以使用lenses 处理JSON 字符串,但作为程序员,我们真的更喜欢处理已知的结构良好的数据,不是吗?这更容易阅读、写作和思考。如果 JSON 结构的某些部分未知/可选,您可以在案例类中将它们设置为 Option[T]。如果可能,您应该始终使用案例类!有很多原因超出了本评论的范围。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-23
  • 2018-09-12
  • 2017-01-13
  • 1970-01-01
  • 1970-01-01
  • 2018-03-15
  • 2022-07-13
相关资源
最近更新 更多