Clojure哈希映射到xml

时间:2012-07-30 21:29:20

标签: xml clojure hashmap

我正在尝试将以下映射转换为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。

2 个答案:

答案 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

我没有手头的转换功能,但是使用这种数据结构,您应该能够编写将哈希映射转换为指定排序的数组映射的内容。 (您也可以使用它来转换为指定排序的有序映射,但同样,它将通过以不雅的方式定义谓词 - 在我看来。)