无法使用clojure.data.zip.xml访问深层嵌套的XML

时间:2017-10-03 00:12:33

标签: xml clojure zipper

(def testxml2
"<top>
    <group>
        <group>
            <item>
                <number>1</number>
            </item>
            <item>
                <number>2</number>
            </item>
            <item>
                <number>3</number>
            </item>
        </group>
        <item>
            <number>0</number>
        </item>
    </group>
</top>")

(def txml2 (zip-str testxml2))

(defn deep-items [x]
    (zip-xml/xml-> x
        :top
        :group
        :group
        :item))

(count (deep-items txml2))
;; 1

(zip-xml/text (first (deep-items txml2)))
;; "0"

我试图获取内部:group的值,但它似乎被外部的一个抓住了。它似乎忽略了第二个:group

我尝试解析的实际XML有一个重复的嵌套<TheirTag><TheirTag>Foo</TheirTag></TheirTag>模式,我需要单独访问每个Foo。 XML来自第三方,因此我无法重构XML以避免这种情况。

2 个答案:

答案 0 :(得分:1)

The reason for the bug is here。对于简短版本:版本0.1.2在这方面略有不同,无法通过tag=函数(:myTag样式选择器的基础)选择具有相同名称的子条目。这是由于从0.1.1到0.1.2的回归(感谢@bpeter和@shilder)。解决方法是在某个util命名空间中创建一个函数tag=并直接使用它直到修复回归。

;; util.clj
(defn tag=
  "This is a workaround to a regression in 0.1.2. Fixed in upcoming 1.2.0

  Returns a query predicate that matches a node when its is a tag
  named tagname."
  [tagname]
  (fn [loc]
    (filter #(and (zip/branch? %) (= tagname (:tag (zip/node %))))
       (zf/children-auto loc))))

;; project.somefile.clj
(ns project.somefile
  (:require [project.util :as u]))


(defn deep-items [x]
    (zip-xml/xml-> x
        :top
        (u/tag= :group)
        (u/tag= :group)
        :item))

答案 1 :(得分:0)

您可以使用the Tupelo Forest library来解决此问题,以处理树状数据结构。除了显式搜索外,它还可以使用zsh之类的通配符。 Documentation is ongoing,但这会让你体验到你可以做的事情:

(dotest
  (with-forest (new-forest)
    (let [xml-str         "<top>
                              <group>
                                  <group>
                                      <item>
                                          <number>1</number>
                                      </item>
                                      <item>
                                          <number>2</number>
                                      </item>
                                      <item>
                                          <number>3</number>
                                      </item>
                                  </group>
                                  <item>
                                      <number>0</number>
                                  </item>
                              </group>
                          </top>"

          enlive-tree     (->> xml-str
                            java.io.StringReader.
                            en-html/xml-resource
                            only)
          root-hid        (add-tree-enlive enlive-tree)

          ; Removing whitespace nodes is optional; just done to keep things neat
          blank-leaf-hid? (fn fn-blank-leaf-hid?  ; whitespace pred fn
                            [hid]
                            (let [node (hid->node hid)]
                              (and (contains-key? node :value)
                                (ts/whitespace? (grab :value node)))))
          blank-leaf-hids (keep-if blank-leaf-hid? (all-leaf-hids)) ; find whitespace nodes
          >>              (apply remove-hid blank-leaf-hids) ; delete whitespace nodes found

你真正关心的部分就在这里。有两种方法可以搜索嵌套节点。

  1. 第一种方法指定来自根
  2. 的显式路径
  3. 第二个使用像zsh这样的通配符:**,匹配零个或多个目录。

          ; Can search for inner `div` 2 ways
          result-1        (find-paths root-hid [:top :group :group]) ; explicit path from root
          result-2        (find-paths root-hid [:** :group :item :number]) ; wildcard path that ends in :number
          ]
    

    对于演员表(1),我们发现我们只找到了第1,2和3项:

      ; Here we see only the double-nested items 1, 2, 3
      (is= (spyx-pretty (format-paths result-1))
        [[{:tag :top}
          [{:tag :group}
           [{:tag :group}
            [{:tag :item} [{:tag :number, :value "1"}]]
            [{:tag :item} [{:tag :number, :value "2"}]]
            [{:tag :item} [{:tag :number, :value "3"}]]]]]] )
    
  4. 对于案例(2),我们不仅发现了双嵌套项,还发现了单嵌套项0

          ; Here we see both the double-nested items & the single-nested item 0
          (is= (spyx-pretty (format-paths result-2))
            [[{:tag :top}
              [{:tag :group} [{:tag :item} [{:tag :number, :value "0"}]]]]
             [{:tag :top}
              [{:tag :group}
               [{:tag :group} [{:tag :item} [{:tag :number, :value "1"}]]]]]
             [{:tag :top}
              [{:tag :group}
               [{:tag :group} [{:tag :item} [{:tag :number, :value "2"}]]]]]
             [{:tag :top}
              [{:tag :group}
               [{:tag :group} [{:tag :item} [{:tag :number, :value "3"}]]]]]])
    
          )))
    

    您没有指定所需的下游处理。 Tupelo.Forest能够将输出转换为hiccupenlive两种格式,以及它自己的打嗝灵感bush格式以及一种活跃的tree格式格式。