使用Clojure XML Zipper返回多个值

4
假设我们有如下的XML代码:
<a>
  <b>
    <c>text</c>
    <d>
      <e>text</e>
      <f>
        ... lots of cruft here ..
      </f>
    </d>
  </b>
  <b>
    ...
  </b>
  <!-- more b sub-trees --> 
</a>

现在,通过查看zip_filter/xml.clj中的示例,我已经弄清楚了如何获取我感兴趣的单个值。

我想知道如何返回(c e)文本值对的列表。

编辑:

这里有一些可行的代码,但它非常丑陋。不是要求微小的重构,但是否有更好的方法可以使用zippers来完成这个任务?

(defn extract-data [xml] 
  (let [items (x/xml-> xml zf/descendants :Item)     ;items not top-level
        getAttributes  #(x/xml1-> % :ItemAttributes) ;items have itemattributes
        getASIN        #(x/xml1-> % :ASIN x/text)    ;items have ASINs
        getTitle       #(x/xml1-> % :Title x/text)   ;itemattributes have Titles
        getAuthor      #(x/xml1-> % :Author x/text)] ;itemattributes have Authors
    (map 
       ;build a function to get everything we need from the items, and apply
      #(let [attributes (getAttributes %)] ;get the attributes, we'll use it twice
         (list 
           (getASIN %) 
           (getTitle attributes) 
           (getAuthor attributes)))
      items)))
2个回答

4

根据您使用的Clojure版本,您可能会发现juxt函数很有用。您发布的代码(仅相关部分):

(defn extract-data
  [xml] 
  (let [...]
    (map (juxt getASIN (comp getTitle getAttributes) (comp getAuthor getAttributes)) items))))

+1 感谢您向我展示了 juxt 和更清晰的布局方式。我很快会尝试它。 - Mark Bolusmjak
很好。我也可以这样做,以避免调用2次getAttributes。(map (juxt getASIN (comp (juxt getTitle getAuthor) getAttributes)) items)但是我必须展平向量。 - Mark Bolusmjak

2

我相信有更好的方法,但这个可以完成任务:

(letfn [(get-tag [tag coll] (:content (first (filter #(= tag (:tag %)) coll))))]
  (map #(list (get-tag :c %) (get-tag :e (get-tag :d %)))
       (map :content (:content (clojure.xml/parse "foo.xml")))))

导致
((["ctext1"] ["etext1"]) (["ctext2"] ["etext2"]))

谢谢,我刚刚发布了一些实际数据结构的代码。感谢您向我展示letfn以及其中一种实现方式。 - Mark Bolusmjak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接