【问题标题】:Can't access deeply nested XML with clojure.data.zip.xml无法使用 clojure.data.zip.xml 访问深度嵌套的 XML
【发布时间】:2017-10-03 00:12:32
【问题描述】:
(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 有一个重复的嵌套 &lt;TheirTag&gt;&lt;TheirTag&gt;Foo&lt;/TheirTag&gt;&lt;/TheirTag&gt; 模式,我需要单独访问每个 Foo。 XML 来自第三方,所以我不能只是重组 XML 来避免这种情况。

【问题讨论】:

    标签: xml clojure zipper


    【解决方案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))
    

    【讨论】:

      【解决方案2】:

      您可以使用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. 第二个使用通配符:**,如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"}]]]]]] )
        

      对于情况(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 格式和enlive 式tree 格式。

      【讨论】:

      • 为非常酷的图书馆投票(加了书签!)。不幸的是,我已经使用 zip-xml 写出了几乎整个项目,此时切换马会很痛苦。感谢您花时间分享这些示例!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      • 2013-02-11
      • 1970-01-01
      • 2017-03-11
      • 2014-07-09
      • 2021-01-03
      相关资源
      最近更新 更多