如何解决如何在 Clojure 中将 XML 转换为 edn?
我是 Clojure 的新手,想将我拥有的 XML 转换为 edn 对象。
我读取的 XML 文件:
<Vehicle>
<Model>Toyota</Model>
<Color>Red</Color>
<Loans>
<Reoccuring>Monthly</Reoccuring>
<Owners>
<Owner>Bob</Owner>
</Owners>
</Loans>
<Tires>
<Model>123123</Model>
<Size>23</Size>
</Tires>
<Engine>
<Model>30065</Model>
</Engine>
</Vehicle>
我把它保存为'test/resources/vehicle.xml
最终,我想要一个看起来像这样的 EDN 对象:
:Vehicle
:Model "Toyota"
:Color "Red"
:Loans
:Reoccuring "Monthly"
:Owners
:Owner "Bob"
:Tires
:Model 123123
:Size 23
:Engine
:Model 30065
到目前为止,我在 Clojure 中尝试过的是 parse 方法:
(def xml-parser
(parse "<Vehicle><Model>Toyota</Model><Color>Red</Color><Loans><Reoccuring>Monthly</Reoccuring><Owners><Owner>Bob</Owner></Owners></Loans><Tires><Model>123123</Model><Size>23</Size></Tires><Engine><Model>30065</Model></Engine></Vehicle>"))
然而,这会返回一个 Clojure 散列,如下所示:
{:tag :Vehicle,:attrs nil,:content [{:tag :Model,:content ["Toyota"]} {:tag :Color,:content ["Red"]} {:tag :Loans,:content [{:tag :Reoccuring,:content ["Monthly"]} {:tag :Owners,:content [{:tag :Owner,:content ["Bob"]}]}]} {:tag :Tires,:content ["123123"]} {:tag :Size,:content ["23"]}]} {:tag :Engine,:content ["30065"]}]}]}
我在转换的初始步骤中遇到了问题。提前感谢您的帮助。
解决方法
您拥有的数据为 Enlive 格式。使用 clojure.pprint/pprint
查看更好的格式:
{:tag :Vehicle,:attrs nil,:content
[{:tag :Model,:content ["Toyota"]}
{:tag :Color,:content ["Red"]}
{:tag :Loans,:content
[{:tag :Reoccuring,:content ["Monthly"]}
{:tag :Owners,:content [{:tag :Owner,:content ["Bob"]}]}]}
{:tag :Tires,:content
[{:tag :Model,:content ["123123"]}
{:tag :Size,:content ["23"]}]}
{:tag :Engine,:content [{:tag :Model,:content ["30065"]}]}]}
问题是您想要的输出实际上并不是合法的 EDN 数据格式。但是,您可以使用 tupelo.forest
库在多种数据格式之间进行转换:
首先声明数据并解析成Enlive格式:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[tupelo.parse.xml :as xml]
[tupelo.forest :as tf])
)
(def xml-str
"<Vehicle>
<Model>Toyota</Model>
<Color>Red</Color>
<Loans>
<Reoccuring>Monthly</Reoccuring>
<Owners>
<Owner>Bob</Owner>
</Owners>
</Loans>
<Tires>
<Model>123123</Model>
<Size>23</Size>
</Tires>
<Engine>
<Model>30065</Model>
</Engine>
</Vehicle> ")
验证结果
(dotest
(let [data-enlive (xml/parse xml-str)]
(is= data-enlive
{:tag :Vehicle,:attrs {},:attrs {},:content ["Toyota"]}
{:tag :Color,:content ["Red"]}
{:tag :Loans,:content [{:tag :Reoccuring,:content ["Monthly"]}
{:tag :Owners,:content ["Bob"]}]}]}
{:tag :Tires,:content ["123123"]}
{:tag :Size,:content ["23"]}]}
{:tag :Engine,:content ["30065"]}]}]})
转换为打嗝格式:
(is= (tf/enlive->hiccup data-enlive)
[:Vehicle
[:Model "Toyota"]
[:Color "Red"]
[:Loans [:Reoccuring "Monthly"]
[:Owners [:Owner "Bob"]]]
[:Tires [:Model "123123"]
[:Size "23"]]
[:Engine [:Model "30065"]]])
您可能还喜欢“灌木”格式:
(is= (tf/enlive->bush data-enlive)
[{:tag :Vehicle}
[{:tag :Model,:value "Toyota"}]
[{:tag :Color,:value "Red"}]
[{:tag :Loans}
[{:tag :Reoccuring,:value "Monthly"}]
[{:tag :Owners} [{:tag :Owner,:value "Bob"}]]]
[{:tag :Tires}
[{:tag :Model,:value "123123"}]
[{:tag :Size,:value "23"}]]
[{:tag :Engine} [{:tag :Model,:value "30065"}]]])
或更详细的“树”格式
(is= (tf/enlive->tree data-enlive)
{:tag :Vehicle,:tupelo.forest/kids
[{:tag :Model,:value "Toyota",:tupelo.forest/kids []}
{:tag :Color,:value "Red",:tupelo.forest/kids []}
{:tag :Loans,:tupelo.forest/kids
[{:tag :Reoccuring,:value "Monthly",:tupelo.forest/kids []}
{:tag :Owners,:tupelo.forest/kids
[{:tag :Owner,:value "Bob",:tupelo.forest/kids []}]}]}
{:tag :Tires,:tupelo.forest/kids
[{:tag :Model,:value "123123",:tupelo.forest/kids []}
{:tag :Size,:value "23",:tupelo.forest/kids []}]}
{:tag :Engine,:value "30065",:tupelo.forest/kids []}]}]})
))
见Tupelo Forest docs 获取完整信息。
以上代码是使用 this template project 运行的。
如果您正在寻找分层地图样式输出,您可以像这样拼凑:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require [clojure.walk :as walk]))
(dotest
(let [data [:Vehicle
[:Model "Toyota"]
[:Color "Red"]
[:Loans
[:Reoccuring "Monthly"]
[:Owners
[:Owner "Bob"]]]
[:Tires
[:Model "123123"]
[:Size "23"]]
[:Engine
[:Model "30065"]]]
mappy (walk/postwalk
(fn [item]
(if (vector? item)
(if (= 2 (count item))
(conj {} item)
{(first item)
(into {} (rest item))})
item))
data)]
带测试
(is= mappy
{:Vehicle
{:Model "Toyota",:Color "Red",:Loans {:Reoccuring "Monthly"
:Owners {:Owner "Bob"}},:Tires {:Model "123123"
:Size "23"},:Engine {:Model "30065"}}})))
虽然写的很脆弱。
,您获得的输出是在 Clojure 中对 XML 文档建模的最灵活方式。正如艾伦·汤普森 (Alan Thompson) 所指出的,它通常被称为 Enlive,因为 enlive
是普及此模型的库。不清楚您还希望得到什么,因为您的预期输出只是一堆没有结构的关键字。您可能一直希望使用 Hiccup 风格(在 Alan Thompson 的回答中再次描述)或嵌套地图(在 Alan Thompson 的回答中称为“mappy”),但如果是这样,我敦促您重新考虑。 Hiccup 的唯一优点是易于手写。 Enlive 更易于使用和转换。
mappy 格式使用起来非常方便,但遗憾的是它不能与 XML 文档 1:1 对应,因为那些可能有重复的元素名称,并且因为 mappy 无法描述属性,只有元素。因此,XML 解析器无法在不丢失保真度的情况下将其作为输出格式提供。如果您知道您的输入没有任何这些问题,您可以自己从 Enlive 编写一个转换函数 - 对于固定架构来说非常容易。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。