Clojure哈希映射到xml [英] Clojure hash-map to xml

查看:50
本文介绍了Clojure哈希映射到xml的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将以下映射转换为xml(具有向量值的任何键都需要针对向量中的每个元素重复xml中的键)

I am trying to convert the following maps to xml (any key with a vector value needs to repeat the key in the xml for each element in the vector)

(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>

当我真正需要通过肥皂发送的东西是

when what i really need to send over via soap is

<?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调用,然后使用响应更新电子表格。
上面是生成Web服务调用所需的xml。

I am trying to read data from excel file and for each row or group of rows with identical id make webservice call, then update the spreadsheet with the response. The above is to generate the xml needed for the webservice call.

推荐答案

您的主要问题,您可能已经知道,当内容(即程序主体中的 v )是矢量时,您必须执行某种(对于... )(map ...)表达式来真正表达其所有标签和内容。但是,这样做会生成标签的序列,这些标签打包在讨厌的括号内。据我所知,您将需要对它们进行无限制处理,以获取正确的结构以传递给(emit-element ...)

Your main problem, which you probably already know, is that when the content (i.e. v in the program body) is a vector, you have to do some sort of (for ...) or (map ...) expression to truly express all of the tags and content of it. However, in so doing, you generate a sequence of tags, which is packaged inside pesky parens. From what I can tell, you would need to "unparens" those in order to obtain the correct structure to pass to (emit-element ...).

在下面的代码中,表达式(mapcat to-xml ...)出现在需要嵌套的地方,因为这将对连续项进行操作,然后将它们全部串联在一起。不幸的是,然后您必须将曾经是单项返回的内容放入向量(或列表)(如果需要)。这就是为什么当(map?v)为真或当:else 发生时,整个(tag-xml ...)表达式包装在向量中。任何退货都将与其他退货一起 concat

Hence in my code below, the expression (mapcat to-xml ...) is in the places where nesting is necessary, because this will perform the operation down successive items, and then concatenate them all together. Unfortunately, you then have to put what used to be single item returns inside vectors (or lists if you wanted). That is why when (map? v) is true or when :else happens, the whole (tag-xml ...) expression is wrapped in a vector. Any returns will be concated with other returns.

我想我已经找到了适合您的东西。我认为这不是 great ,因为我不喜欢它如何处理顶级通话-即您将在代码中进行的调用(但稍后再讲):

I think I've found something that will work for you. It's not great, in my opinion, because I don't like how it handles the top-level call--i.e. the call you would make in your code (but I'll get to that later):

(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 的主体更小和更小。

Notice that I added a helper function tag-xml. This was just to make the body of to-xml cleaner and smaller.

这是您可能会使用的方式(尽管在您的情况下,您可以将 println 替换为某些 spit 调用):

This is how you might use it (although in your case, you would replace println with some spit call):

=> (->> 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

所以,我不喜欢为了从顶层正确地调用它,一些现有的哈希映射数据,则需要执行(应用于XML(第一个数据))。您可以可以解决此问题,而不是将数据作为哈希映射,而是将其构造为矢量。在您的示例中,这看起来像 [:company ...] 而不是 {:company ...} studios 中的每个工作室。然后,您可以使用如下函数:(第一个(到xml数据))

So, I don't like that in order to call it from the top level properly, to some existing hash-map data, you would need to do (apply to-xml (first data)). You could get around this by instead of having your data as a hash-map, structure it as a vector. In you example this would look like [:company ...] instead of {:company ...} for each studio in studios. Then instead, you could use the function like this: (first (to-xml data)).

仍然,这并不像我希望的那样优雅。也许解决方案是拥有一些函数 to-xml 来进行顶级调用,而另一些函数 -to-xml 之后将处理它。作为用户,您只会使用 to-xml ,但是所有艰苦的工作都将在 -xml 。我也不为这个想法疯狂。另一个想法是做一些类似于您所做的事情,如果第一个参数等于 nil ,那么它的作用就好像是在顶层调用一样。嗯。

Still, this isn't as elegant as I would like it to be. Maybe the solution would be to have some function to-xml that would do the top level call, and some other function -to-xml that would handle it after that. As a user, you would only be using to-xml but all the hard work would be being done in -to-xml. I'm not crazy about that idea either. Yet another idea would be do something somewhat like what you did, where if the first argument equals nil then it does the function as though it were a top level call. Hmm.

无论如何,它可以工作,所以有点。

Anyways, it works, so that's something.

编辑 < br>
至于要保留顺序,您可能必须重新定义数据,或者在使用 to-xml 处理数据之前对其进行转换。您不能依靠任何写为 {...} 的顺序。如果您想将其保留为地图,则可以 将其制成数组地图或排序后的地图。

Edit
As for wanting to preserve the order, you would probably have to either redefine your data, or transform it before processing it with to-xml. You can not rely on the order of anything written as {...}. If you want to keep it as a map, then you could get order by making it an array map or a sorted map.

如果要重新定义它以使其成为数组映射,它将看起来像这样:

If you were to redefine it to make it an array map, it would look something like this:

(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-地图...)。在这一点上,我应该说,不要费心尝试编写一个宏来为您执行此操作,它将无法正常工作(有关此问题的信息,请参见此处)。如果要使用排序的映射,则必须创建一个谓词比较器,该比较器只返回 true false 基于一些硬编码的排序,这对我来说似乎有点奇怪。

Basically, anywhere you used to have {...} you now have (array-map ...). At this point, I should say, don't bother try writing a macro to do that for you, it won't work (see here for my question on this). If you want to go with using a sorted map, you will have to make a predicate comparator that just returns true or false based on some hard-coded ordering, and this seems a little odd to me.

现在,如果要转换数据,则需要另一个数据结构,包含关键订单和嵌套订单。像这样的东西:

Now, if you want to transform the data, you will need another data structure that contains key orders as well as nested orders. Something like:

(def studio-order-specs {:company [:name :id :rank {:employee [:lname :fname:]}]})

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

I don't have the transforming function at hand, but using this data structure, you should be able to write something that converts a hash map to an array map of the specified ordering. (You could also use this to convert to a sorted map of the specified ordering, but again, it would be through defining a predicate in an inelegant way--in my opinion.)

这篇关于Clojure哈希映射到xml的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆