- xml - AJAX/Jquery XML 解析
- 具有多重继承的 XML 模式
- .net - 枚举序列化 Json 与 XML
- XML 简单类型、简单内容、复杂类型、复杂内容
为了使用 circe 将 JSON 节点转换为 JSON 以外的其他格式(如 XML、CSV 等),我想出了一个解决方案,我必须访问 circe 的内部数据结构。
这是我将 JSON 转换为 XML 字符串的工作示例(并不完美,但您明白了):
package io.circe
import io.circe.Json.{JArray, JBoolean, JNull, JNumber, JObject, JString}
import io.circe.parser.parse
object Sample extends App {
def transformToXMLString(js: Json): String = js match {
case JNull => ""
case JBoolean(b) => b.toString
case JNumber(n) => n.toString
case JString(s) => s.toString
case JArray(a) => a.map(transformToXMLString(_)).mkString("")
case JObject(o) => o.toMap.map {
case (k, v) => s"<${k}>${transformToXMLString(v)}</${k}>"
}.mkString("")
}
val json =
"""{
| "root": {
| "sampleboolean": true,
| "sampleobj": {
| "anInt": 1,
| "aString": "string"
| },
| "objarray": [
| {"v1": 1},
| {"v2": 2}
| ]
| }
|}""".stripMargin
val res = transformToXMLString(parse(json).right.get)
println(res)
}
结果:
<root><sampleboolean>true</sampleboolean><sampleobj><anInt>1</anInt><aString>string</aString></sampleobj><objarray><v1>1</v1><v2>2</v2></objarray></root>
如果低级 JSON 对象(如 JBoolean、JString、JObject
等)不是 package private in circe,那一切都很好而且花花公子,这只会使这段代码如果将其放在包 package io.circe
中,则上述工作。
如何使用公共(public) circe API 获得与上述相同的结果?
最佳答案
Json
上的fold
方法允许您非常简洁地执行此类操作(并且以一种强制穷举的方式,就像对密封特征进行模式匹配一样) :
import io.circe.Json
def transformToXMLString(js: Json): String = js.fold(
"",
_.toString,
_.toString,
identity,
_.map(transformToXMLString(_)).mkString(""),
_.toMap.map {
case (k, v) => s"<${k}>${transformToXMLString(v)}</${k}>"
}.mkString("")
)
然后:
scala> import io.circe.parser.parse
import io.circe.parser.parse
scala> transformToXMLString(parse(json).right.get)
res1: String = <root><sampleboolean>true</sampleboolean><sampleobj><anInt>1</anInt><aString>string</aString></sampleobj><objarray><v1>1</v1><v2>2</v2></objarray></root>
与您的实现结果完全相同,但字符少一些,并且不依赖于实现的私有(private)细节。
所以答案是“使用 fold
”(或另一个答案中建议的 asX
方法——这种方法更灵活,但通常可能更少惯用且更冗长)。如果你关心我们为什么设计决定不暴露构造函数,你可以跳到这个答案的末尾,但是这种问题经常出现,所以我也想解决一些相关的问题首先。
请注意,此方法使用的名称“fold”是从 Argonaut 继承而来的,可以说是不准确的。当我们谈论递归代数数据类型的变形(或折叠)时,我们指的是一个函数,我们在传入的函数的参数中看不到 ADT 类型。例如,列表折叠的签名看起来像这样:
def foldLeft[B](z: B)(op: (B, A) => B): B
不是这个:
def foldLeft[B](z: B)(op: (List[A], A) => B): B
因为 io.circe.Json
是一个递归的 ADT,它的 fold
方法应该是这样的:
def properFold[X](
jsonNull: => X,
jsonBoolean: Boolean => X,
jsonNumber: JsonNumber => X,
jsonString: String => X,
jsonArray: Vector[X] => X,
jsonObject: Map[String, X] => X
): X
代替:
def fold[X](
jsonNull: => X,
jsonBoolean: Boolean => X,
jsonNumber: JsonNumber => X,
jsonString: String => X,
jsonArray: Vector[Json] => X,
jsonObject: JsonObject => X
): X
但在实践中前者似乎用处不大,所以circe只提供了后者(如果要递归,则必须手动完成),并沿袭了Argonaut的说法,将其称为fold
。这一直让我有点不舒服,以后这个名字可能会改变。
在某些情况下,实例化 fold
期望的六个函数可能非常昂贵,因此 circe 还允许您将这些操作捆绑在一起:
import io.circe.{ Json, JsonNumber, JsonObject }
val xmlTransformer: Json.Folder[String] = new Json.Folder[String] {
def onNull: String = ""
def onBoolean(value: Boolean): String = value.toString
def onNumber(value: JsonNumber): String = value.toString
def onString(value: String): String = value
def onArray(value: Vector[Json]): String =
value.map(_.foldWith(this)).mkString("")
def onObject(value: JsonObject): String = value.toMap.map {
case (k, v) => s"<${k}>${transformToXMLString(v)}</${k}>"
}.mkString("")
}
然后:
scala> parse(json).right.get.foldWith(xmlTransformer)
res2: String = <root><sampleboolean>true</sampleboolean><sampleobj><anInt>1</anInt><aString>string</aString></sampleobj><objarray><v1>1</v1><v2>2</v2></objarray></root>
使用 Folder
的性能优势会有所不同,具体取决于您使用的是 2.11 还是 2.12,但是如果您对 JSON 值执行的实际操作很便宜,您可以期望 Folder
版本以获得大约两倍于 fold
的吞吐量。顺便说一下,它也比内部构造函数的模式匹配快得多,至少在 benchmarks we've done 中是这样。 :
Benchmark Mode Cnt Score Error Units
FoldingBenchmark.withFold thrpt 10 6769.843 ± 79.005 ops/s
FoldingBenchmark.withFoldWith thrpt 10 13316.918 ± 60.285 ops/s
FoldingBenchmark.withPatternMatch thrpt 10 8022.192 ± 63.294 ops/s
那是在 2.12 上。我相信您应该在 2.11 上看到更多差异。
如果你真的想要模式匹配,circe-optics为您提供案例类提取器的高性能替代方案:
import io.circe.Json, io.circe.optics.all._
def transformToXMLString(js: Json): String = js match {
case `jsonNull` => ""
case jsonBoolean(b) => b.toString
case jsonNumber(n) => n.toString
case jsonString(s) => s.toString
case jsonArray(a) => a.map(transformToXMLString(_)).mkString("")
case jsonObject(o) => o.toMap.map {
case (k, v) => s"<${k}>${transformToXMLString(v)}</${k}>"
}.mkString("")
}
这几乎与原始版本的代码完全相同,但每个提取器都是一个单片眼镜棱镜,可以与来自 the Monocle library 的其他光学器件组合在一起。 .
(这种方法的缺点是您失去了详尽检查,但不幸的是,这无济于事。)
当我第一次开始研究 circe 时,我在 a document about some of my design decisions 中写了以下内容:
In some cases, including most significantly here the
io.circe.Json
type, we don't want to encourage users to think of the ADT leaves as having meaningful types. A JSON value "is" a boolean or a string or a unit or aSeq[Json]
or aJsonNumber
or aJsonObject
. Introducing types likeJString
,JNumber
, etc. into the public API just confuses things.
我想要一个非常精简的 API(尤其是一个避免暴露无意义类型的 API)并且我想要优化 JSON 表示的空间。 (我也根本不希望人们使用 JSON AST,但那更像是一场失败的战斗。)我仍然认为隐藏构造函数是正确的决定,尽管我没有真正利用它他们(还)没有进行优化,尽管这个问题经常出现。
关于json - 使用 circe 递归地将 JSON 树转换为其他格式(XML、CSV 等),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55074967/
最近开始学习MongoDB。今天老师教了我们 mongoexport 命令。在练习时,我遇到了一个典型的问题,包括教练在内的其他同学都没有遇到过。我在我的 Windows 10 机器上使用 Mongo
我是 JSON Schema 的新手,读过什么是 JSON Schema 等等。但我不知道如何将 JSON Schema 链接到 JSON 以针对该 JSON Schema 进行验证。谁能解释一下?
在 xml 中,我可以在另一个 xml 文件中包含一个文件并使用它。如果您的软件从 xml 获取配置文件但没有任何方法来分离配置,如 apache/ngnix(nginx.conf - site-av
我有一个 JSON 对象,其中包含一个本身是 JSON 对象的字符串。我如何反序列化它? 我希望能够做类似的事情: #[derive(Deserialize)] struct B { c: S
考虑以下 JSON { "a": "{\"b\": 12, \"c\": \"test\"}" } 我想定义一个泛型读取 Reads[Outer[T]]对于这种序列化的 Json import
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 11 个月前关闭。 Improve
我的旧项目在 MySQL 中有 Standard JSON 格式的数据。 对于我在 JS (Node.js) 和 DynamoDB 中的全新项目,关于 Standard JSON格式: 是否建议将其转
JSON 值字符串、数字、true、false、null 是否是有效的 JSON? 即,是 true 一个有效的 JSON 文档?还是必须是数组/对象? 一些验证器接受这个(例如 http://jso
我有一个 JSON 字符串,其中一个字段是文本字段。这个文本字段可以包含用户在 UI 中输入的文本,如果他们输入的文本是 JSON 文本,也许是为了说明一些编码,我需要对他们的文本进行编码,以便它不会
我正在通过 IBM MQ 调用处理数据,当由 ColdFusion 10 (10,0,11,285437) 序列化时,0 将作为 +0.0 返回,它会导致无效的 JSON并且无法反序列化。 stPol
我正在从三个数组中生成一个散列,然后尝试构建一个 json。我通过 json object has array 成功了。 require 'json' A = [['A1', 'A2', 'A3'],
我从 API 接收 JSON,响应可以是 30 种类型之一。每种类型都有一组唯一的字段,但所有响应都有一个字段 type 说明它是哪种类型。 我的方法是使用serde .我为每种响应类型创建一个结构并
我正在下载一个 JSON 文件,我已将其检查为带有“https://jsonlint.com”的有效 JSON 到文档目录。然后我打开文件并再次检查,结果显示为无效的 JSON。这怎么可能????这是
我正在尝试根据从 API 接收到的数据动态创建一个 JSON 对象。 收到的示例数据:将数据解码到下面给出的 CiItems 结构中 { "class_name": "test", "
我想从字符串转换为对象。 来自 {"key1": "{\n \"key2\": \"value2\",\n \"key3\": {\n \"key4\": \"value4\"\n }\n
目前我正在使用以下代码将嵌套的 json 转换为扁平化的 json: import ( "fmt" "github.com/nytlabs/gojsonexplode" ) func
我有一个使用来自第三方 API 的数据的应用程序。我需要将 json 解码为一个结构,这需要该结构具有“传入”json 字段的 json 标签。传出的 json 字段具有不同的命名约定,因此我需要不同
我想使用 JSON 架构来验证某些值。我有两个对象,称它们为 trackedItems 和 trackedItemGroups。 trackedItemGroups 是组名称和 trackedItem
考虑以下案例类模式, case class Y (a: String, b: String) case class X (dummy: String, b: Y) 字段b是可选的,我的一些数据集没有字
我正在存储 cat ~/path/to/file/blah | 的输出jq tojson 在一个变量中,稍后在带有 JSON 内容的 curl POST 中使用。它运作良好,但它删除了所有换行符。我知
我是一名优秀的程序员,十分优秀!