我正在尝试将以下映射转换为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>当我真正需要通过soap发送的内容是
<?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。
发布于 2012-07-31 22:29:53
您可能已经知道,您的主要问题是,当内容(即程序体中的v )是一个向量时,您必须执行某种(for ...)或(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的主体更干净、更小。
您可以这样使用它(尽管在您的示例中,您需要用一些spit调用替换println ):
=> (->> 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))。您可以通过将数据结构化为向量来解决此问题,而不是将数据作为散列映射。在您的示例中,对于studios中的每个工作室,这将类似于[:company ...]而不是{:company ...}。然后,您可以像这样使用函数:(first (to-xml data))。
尽管如此,这并不像我希望的那样优雅。也许解决方案是让一些函数to-xml执行顶层调用,然后让其他一些函数-to-xml来处理它。作为用户,您将只使用to-xml,但所有的繁重工作都将在-to-xml中完成。我对这个想法也不感兴趣。还有另一个想法是做一些像你所做的事情,如果第一个参数等于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")]))])基本上,在过去使用{...}的任何地方,现在都有(array-map ...)。在这一点上,我应该说,不要费心去尝试为你写一个宏来做这件事,它不会工作(see here for my question on this)。如果您想使用排序映射,则必须创建一个谓词比较器,它只根据某种硬编码的顺序返回true或false,这对我来说似乎有点奇怪。
现在,如果要转换数据,则需要另一个包含键订单和嵌套订单的数据结构。类似于:
(def studio-order-specs {:company [:name :id :rank {:employee [:lname :fname:]}]})我手头没有转换函数,但是使用这个数据结构,您应该能够编写一些东西,将散列映射转换为指定顺序的数组映射。(您也可以使用它来转换为指定顺序的排序映射,但在我看来,这也是通过以一种不优雅的方式定义谓词来实现的。)
发布于 2012-07-31 06:47:20
您的输入在映射中的: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"}]}https://stackoverflow.com/questions/11729910
复制相似问题