如何使用Circe公共API实现JSON到XML的递归格式转换?
如何使用Circe公共API实现JSON到XML的转换
你之前的方案确实踩了Circe的一个小坑——直接依赖了包私有的JBoolean、JObject等内部类型,导致代码只能在io.circe包下运行。不过别担心,Circe提供了完整的公共API来遍历和操作JSON结构,完全不需要碰这些私有类型。下面我就给你演示怎么用公共API重写你的转换逻辑:
优化后的实现(使用公共API)
我们可以用Circe Json类型提供的fold方法,它是专门为这种类型分支处理设计的公共方法,完美替代原来的模式匹配:
import io.circe.Json import io.circe.parser.parse object JsonToXmlConverter extends App { def transformToXMLString(js: Json): String = { js.fold( // 处理Null类型 "", // 处理布尔值 booleanValue => booleanValue.toString, // 处理数字 numberValue => numberValue.toString, // 处理字符串 stringValue => stringValue.toString, // 处理数组:递归转换每个元素后拼接 array => array.map(transformToXMLString).mkString(""), // 处理对象:遍历键值对,生成XML标签后拼接 obj => obj.toIterable.map { case (key, value) => s"<${key}>${transformToXMLString(value)}</${key}>" }.mkString("") ) } // 测试用的JSON字符串 val jsonInput = """{ | "root": { | "sampleboolean": true, | "sampleobj": { | "anInt": 1, | "aString": "string" | }, | "objarray": [ | {"v1": 1}, | {"v2": 2} | ] | } |}""".stripMargin // 解析JSON并处理结果(增加错误处理,避免直接get抛出异常) parse(jsonInput) match { case Right(validJson) => println(transformToXMLString(validJson)) case Left(parseError) => println(s"JSON解析失败: ${parseError.getMessage}") } }
代码说明
fold方法的作用:它会根据当前Json实例的实际类型,自动调用对应的处理函数,覆盖了所有可能的JSON类型(Null、布尔、数字、字符串、数组、对象),完全替代了对私有J*类型的模式匹配。- 对象和数组的处理:对于JSON对象,我们用公共方法
toIterable遍历键值对;对于数组,直接用map递归处理每个元素,和你原来的逻辑一致,但完全基于公共API。 - 错误处理优化:把原来的
right.get换成了模式匹配,避免了JSON解析失败时抛出未捕获的异常,代码更健壮。
输出结果
运行这段代码,你会得到和原来完全一样的XML字符串:
<root><sampleboolean>true</sampleboolean><sampleobj><anInt>1</anInt><aString>string</aString></sampleobj><objarray><v1>1</v1><v2>2</v2></objarray></root>
另一种实现方式(类型判断+获取值)
如果你更喜欢用条件判断的风格,也可以用Circe提供的isXxx和asXxx系列公共方法:
def transformToXMLStringAlternative(js: Json): String = { js match { case _ if js.isNull => "" case _ if js.isBoolean => js.asBoolean.get.toString case _ if js.isNumber => js.asNumber.get.toString case _ if js.isString => js.asString.get.toString case _ if js.isArray => js.asArray.get.map(transformToXMLStringAlternative).mkString("") case _ if js.isObject => js.asObject.get.toIterable.map { case (k, v) => s"<${k}>${transformToXMLStringAlternative(v)}</${k}>" }.mkString("") case _ => "" // 理论上不会触发,因为JSON只有上述6种类型 } }
不过这种方式需要注意:asXxx.get会在类型不匹配时抛出异常,而fold方法是类型安全的,强制你处理所有情况,所以更推荐用fold的实现方式。
内容的提问来源于stack exchange,提问作者Urs Peter
相关产品推荐
相关产品推荐

