我正在尝试将以下映射转换为xml(任何带有向量值的键都需要在向量中的每个元素的xml中重复键)
(use 'clojure.xml)
(defn map-to-xml2 [k v]
(cond
(nil? k) (for [[e a] v] {:tag e :content (map-to-xml2 e a)})
(map? v) (for [[e a] v] {:tag e :content (map-to-xml2 e a)})
(vector? v) (for [x v] {:tag k :content (for [[e a] x] {:tag e :content (map-to-xml2 e a)})})
:else [(str v)]))
(def studios [{:company {:name "Acme Corp" :id 1 :rank 20 :employee
[{:fname "Mark" :lname "Jones"} {:fname "Leroy" :lname "Bell"}]}}
{:company {:name "Eastwood Studios" :id 2 :rank 35 :employee
[{:fname "Lee" :lname "Marvin"} {:fname "Clint" :lname "Eastwood"}]}}])
(->> studios first (map-to-xml2 nil) first emit with-out-str (spit "acme.xml"))
(->> studios second (map-to-xml2 nil) first emit with-out-str (spit "eastwood.xml"))
我得到以下xml
<?xml version='1.0' encoding='UTF-8'?>
<company>
<rank>35</rank>
<employee>
<employee>
<lname>Marvin</lname>
<fname>Lee</fname>
</employee>
<employee>
<lname>Eastwood</lname>
<fname>Clint</fname>
</employee>
</employee>
<name>Eastwood Studios</name>
<id>2</id>
</company>
当我真正需要通过肥皂发送的是
<?xml version='1.0' encoding='UTF-8'?>
<company>
<name>Eastwood Studios</name>
<id>2</id>
<rank>35</rank>
<employee>
<lname>Marvin</lname>
<fname>Lee</fname>
</employee>
<employee>
<lname>Eastwood</lname>
<fname>Clint</fname>
</employee>
</company>
我如何纠正上述情况?
我正在尝试从excel文件读取数据,并且对于具有相同id的每行或每组行进行webservice调用,然后使用响应更新电子表格。 以上是生成webservice调用所需的xml。
答案 0 :(得分:1)
您的输入有一个额外的嵌套层,其中的员工列表位于地图中的:employee关键字下。如果将该封闭结构更改为列表,则可以将整个树平整一层。
{:company {:name "Acme Corp" :id 1 :rank 20 :employee
[{:fname "Mark" :lname "Jones"} {:fname "Leroy" :lname "Bell"}]}}
变得像:
{:company [{:name "Acme Corp"}
{:id 1} {:rank 20}
{:fname "Mark" :lname "Jones"}
{:fname "Leroy" :lname "Bell"}]}
答案 1 :(得分:1)
您可能已经知道的主要问题是,当内容(即程序正文中的v
)是向量时,您必须执行某种(for ...)
或{{1表达式来真实地表达它的所有标签和内容。但是,通过这样做,您可以生成标记的序列,这些标记包含在讨厌的parens中。据我所知,你需要“解开”那些,以便获得传递给(map ...)
的正确结构。
因此,在下面的代码中,表达式(emit-element ...)
位于需要嵌套的位置,因为这将执行连续项的操作,然后将它们连接在一起。不幸的是,你必须把过去单个项目返回的内容放在向量(或列表中,如果你想要的话)。这就是为什么当(mapcat to-xml ...)
为真或(map? v)
发生时,整个:else
表达式都包含在向量中。任何退货都将(tag-xml ...)
与其他退货。
我想我找到了适合你的东西。在我看来,它不是伟大的,因为我不喜欢它如何处理顶层呼叫 - 即。你将在你的代码中进行的调用(但我稍后会讨论):
concat
请注意,我添加了辅助函数(defn tag-xml
[tag content]
{:tag tag
:content content})
(defn to-xml
([[k v]] ;//This form of to-xml is for the sake of nested calls
(cond
(map? v) [(tag-xml k (mapcat to-xml v))]
(vector? v) (for [x v] (tag-xml k (mapcat to-xml x)))
:else [(tag-xml k [(str v)])]))
([k v] ;//This form of to-xml is only for the sake of the top level call
(tag-xml k (if (map? v) (mapcat to-xml v) [(str v)]))))
。这只是为了使tag-xml
的身体更清洁,更小。
这是您可以使用它的方式(尽管在您的情况下,您会将to-xml
替换为println
次调用):
spit
所以,我不喜欢这样,为了从顶层正确地调用它,到现有的哈希映射=> (->> studios ffirst (apply to-xml) emit with-out-str println))
<?xml version='1.0' encoding='UTF-8'?>
<company>
<rank>
20
</rank>
<employee>
<lname>
Jones
</lname>
<fname>
Mark
</fname>
</employee>
<employee>
<lname>
Bell
</lname>
<fname>
Leroy
</fname>
</employee>
<name>
Acme Corp
</name>
<id>
1
</id>
</company>
=> nil
,你需要做data
。你可以解决这个问题,而不是将你的数据作为哈希映射,将其构建为向量。在您的示例中,对于(apply to-xml (first data))
中的每个工作室,这看起来像[:company ...]
而不是{:company ...}
。然后,你可以使用这样的函数:studios
。
尽管如此,这并不像我希望的那样优雅。也许解决方案是使用一些函数(first (to-xml data))
进行顶级调用,然后使用其他函数to-xml
来处理它。作为用户,您只能使用-to-xml
,但所有艰苦的工作都将在to-xml
中完成。我对这个想法也不是很疯狂。另一个想法是做一些类似于你所做的事情,如果第一个参数等于-to-xml
那么它就像一个顶级调用那样执行函数。 HMM。
无论如何,它有效,所以这就是。
修改强>
至于想要保留订单,您可能需要重新定义数据,或者在使用nil
处理数据之前对其进行转换。您不能依赖于to-xml
所写的任何顺序。如果你想把它保存为地图,那么可以通过使它成为数组地图或有序地图来获得顺序。
如果要重新定义它以使其成为数组映射,它将如下所示:
{...}
基本上,您曾经拥有(def studios [(array-map :company (array-map :name "Acme Corp" :id 1 :rank 20
:employee [(array-map :fname "Mark" :lname "Jones")
(array-map :fname "Leroy" :lname "Bell")]))
(array-map :company (array-map :name "Eastwood Studios" :id 2 :rank 35
:employee [(array-map :fname "Lee" :lname "Marvin")
(array-map :fname "Clint" :lname "Eastwood")]))])
的任何地方现在都拥有{...}
。在这一点上,我应该说,不要试图写一个宏来为你做这件事,它不会起作用(see here for my question on this)。如果你想使用有序地图,你必须根据一些硬编码顺序创建一个只返回(array-map ...)
或true
的谓词比较器,这对我来说似乎有些奇怪。
现在,如果要转换数据,则需要另一个包含键顺序和嵌套订单的数据结构。类似的东西:
false
我没有手头的转换功能,但是使用这种数据结构,您应该能够编写将哈希映射转换为指定排序的数组映射的内容。 (您也可以使用它来转换为指定排序的有序映射,但同样,它将通过以不雅的方式定义谓词 - 在我看来。)