2017-10-03 1 views
0
(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" 

J'essaie d'obtenir la valeur de la :group intérieure, mais il semble être de se faire prendre à l'un à l'extérieur. Il semble ignorer la deuxième :group.Impossible d'accéder profondément XML imbriqué avec clojure.data.zip.xml

Le XML réel que j'essaye d'analyser a un motif <TheirTag><TheirTag>Foo</TheirTag></TheirTag> imbriqué répété qui se passe et j'ai besoin d'accéder à chaque Foo individuellement. Le XML provient d'un tiers, donc je ne peux pas simplement restructurer le XML pour éviter cela.

Répondre

1

The reason for the bug is here. Pour la version courte: la version 0.1.2 est légèrement cassée à cet égard, une sous-entrée portant le même nom ne peut pas être sélectionnée via la fonction tag= (qui sous-tend les sélecteurs de style :myTag.) Ceci est dû à une régression de 0.1.1 à 0.1.2 (@bpeter merci et @shilder). la solution consiste à faire une fonction tag= dans certains espace de noms util et l'utiliser directement jusqu'à ce que la régression est fixée.

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

Vous pouvez résoudre cela en utilisant the Tupelo Forest library pour traiter En plus de la recherche explicite, il peut également utiliser des caractères génériques comme zsh, Documentation is ongoing, mais cela vous donnera un avant-goût de ce que vous pouvez faire:

(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 

La partie qui vous intéresse vraiment est ici. Il existe deux façons de rechercher des noeuds imbriqués.

  1. La première méthode spécifie un chemin explicite de la racine
  2. Le second utilise un caractère générique :** comme zsh, qui correspond à zéro ou plusieurs répertoires.

     ; 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 
         ] 
    

    Pour tuyaux en fonte (1), nous voyons que nous avons trouvé des éléments seulement 1, 2 et 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"}]]]]]]) 
    

Dans le cas (2), nous avons trouvé non seulement le doublement imbriquée articles, mais aussi l'élément seul imbriquée 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"}]]]]]]) 

    ))) 

vous ne précisaient pas ce que vous en aval traitement nécessaire. Tupelo.Forest est capable de convertir la sortie en formats hiccup et enlive, en plus de son propre format bush inspiré du hiccup et un format inspiré de tree.

+0

Refusé pour la bibliothèque vraiment cool (signet!). Malheureusement, j'ai déjà écrit presque tout le projet en utilisant zip-xml et en changeant de chevaux à ce stade serait une douleur. Merci d'avoir pris le temps de partager ces exemples! – BWStearns