使用Scala解析嵌套JSON:遥测数据生成的多层JSON字段提取问题
Scala解析嵌套Telemetry JSON的解决方案
我完全理解你现在的困扰——多层嵌套的JSON确实容易让人头疼,尤其是Telemetry这种结构固定但层级深的数据。下面我给你分享两种Scala生态里最常用的JSON解析方案,分别基于Play JSON和Circe这两个主流库,你可以根据自己项目的依赖情况选择合适的实现方式。
方案一:使用Play JSON(Typesafe官方库)
Play JSON是Scala项目中非常常用的JSON处理库,API直观,和Play框架集成友好,当然也可以单独使用。
第一步:添加依赖
在你的build.sbt里加入Play JSON的依赖(版本可以根据需要调整):
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.2"
第二步:定义数据模型
根据你的Telemetry JSON结构,创建对应的case class来映射层级结构。这里假设attributes里有interfaceName和status两个字段,你可以根据实际字段调整:
import play.api.libs.json._ // 映射attributes层级 case class InterfaceAttributes(interfaceName: String, status: String) // 映射interfaceEntity层级 case class InterfaceEntity(attributes: InterfaceAttributes) // 映射最外层的Telemetry数据 case class TelemetryData( version_str: String, node_id_str: String, encoding_path: String, collection_id: Long, collection_start_time: Long, collection_end_time: Long, msg_timestamp: Long, subscription_id: List[String], sensor_group_id: List[String], data_source: String, data: Map[String, InterfaceEntity] // 用Map来接收data下的动态key(比如interfaceEntity) ) // 自动生成JSON格式转换器(implicit用于Play JSON的自动解析) implicit val interfaceAttributesFormat: OFormat[InterfaceAttributes] = Json.format[InterfaceAttributes] implicit val interfaceEntityFormat: OFormat[InterfaceEntity] = Json.format[InterfaceEntity] implicit val telemetryDataFormat: OFormat[TelemetryData] = Json.format[TelemetryData]
第三步:解析并提取字段
接下来就可以解析JSON字符串,然后提取你需要的特定字段了:
// 你的Telemetry JSON示例字符串 val telemetryJson = """{ "version_str": "1.0.0", "node_id_str": "router-01", "encoding_path": "sys/intf", "collection_id": 241466, "collection_start_time": 0, "collection_end_time": 0, "msg_timestamp": 0, "subscription_id": [ ], "sensor_group_id": [ ], "data_source": "DME", "data": { "interfaceEntity": { "attributes": {"interfaceName": "GigabitEthernet0/0", "status": "up"} } } }""" // 解析JSON字符串 val parseResult = Json.parse(telemetryJson).validate[TelemetryData] // 处理解析结果 parseResult match { case JsSuccess(telemetry, _) => // 提取你需要的字段,比如接口名称和状态 val targetEntity = telemetry.data("interfaceEntity") println(s"接口名称: ${targetEntity.attributes.interfaceName}") println(s"接口状态: ${targetEntity.attributes.status}") // 其他字段同理,直接从telemetry对象中获取 case JsError(errors) => println(s"解析失败,错误详情: $errors") }
方案二:使用Circe(函数式风格库)
Circe是基于Cats的函数式JSON库,适合喜欢函数式编程风格的开发者,API更灵活,支持自动推导和手动编写编解码器。
第一步:添加依赖
在build.sbt里加入Circe的核心、泛型推导和解析器依赖:
libraryDependencies ++= Seq( "io.circe" %% "circe-core" % "0.14.6", "io.circe" %% "circe-generic" % "0.14.6", "io.circe" %% "circe-parser" % "0.14.6" )
第二步:定义数据模型与编解码器
同样先定义case class,然后用Circe的泛型推导自动生成解码器:
import io.circe._ import io.circe.generic.semiauto._ import io.circe.parser.decode case class InterfaceAttributes(interfaceName: String, status: String) case class InterfaceEntity(attributes: InterfaceAttributes) case class TelemetryData( version_str: String, node_id_str: String, encoding_path: String, collection_id: Long, collection_start_time: Long, collection_end_time: Long, msg_timestamp: Long, subscription_id: List[String], sensor_group_id: List[String], data_source: String, data: Map[String, InterfaceEntity] ) // 自动推导解码器 implicit val interfaceAttributesDecoder: Decoder[InterfaceAttributes] = deriveDecoder[InterfaceAttributes] implicit val interfaceEntityDecoder: Decoder[InterfaceEntity] = deriveDecoder[InterfaceEntity] implicit val telemetryDataDecoder: Decoder[TelemetryData] = deriveDecoder[TelemetryData]
第三步:解析并提取字段
用Circe的decode方法解析JSON,然后处理结果:
val telemetryJson = """{ "version_str": "1.0.0", "node_id_str": "router-01", "encoding_path": "sys/intf", "collection_id": 241466, "collection_start_time": 0, "collection_end_time": 0, "msg_timestamp": 0, "subscription_id": [ ], "sensor_group_id": [ ], "data_source": "DME", "data": { "interfaceEntity": { "attributes": {"interfaceName": "GigabitEthernet0/0", "status": "up"} } } }""" // 解析JSON decode[TelemetryData](telemetryJson) match { case Right(telemetry) => val targetAttrs = telemetry.data("interfaceEntity").attributes println(s"提取到节点ID: ${telemetry.node_id_str}") println(s"接口状态: ${targetAttrs.status}") case Left(error) => println(s"解析出错: ${error.getMessage}") }
额外提示:处理动态/未知字段
如果你的attributes字段是动态的(不确定具体有哪些字段),可以把attributes的类型定义为JsValue(Play JSON)或Json(Circe),然后手动提取字段:
Play JSON示例
case class InterfaceEntity(attributes: JsValue) // 提取字段 val status = (telemetry.data("interfaceEntity").attributes \ "status").asOpt[String].getOrElse("unknown")
Circe示例
case class InterfaceEntity(attributes: Json) // 提取字段 val status = telemetry.data("interfaceEntity").attributes.hcursor.get[String]("status").getOrElse("unknown")
这样即使字段有变化,也能灵活处理,避免解析失败。
内容的提问来源于stack exchange,提问作者user3521305
相关产品推荐
相关产品推荐

