在Clojure中搜索xml

pis*_*hio 11 xml clojure

我有以下示例xml:

<data>
  <products>
    <product>
      <section>Red Section</section>
      <images>
        <image>img.jpg</image>
        <image>img2.jpg</image>
      </images>
    </product>
    <product>
      <section>Blue Section</section>
      <images>
        <image>img.jpg</image>
        <image>img3.jpg</image>
      </images>
    </product>
    <product>
      <section>Green Section</section>
      <images>
        <image>img.jpg</image>
        <image>img2.jpg</image>
      </images>
    </product>
  </products>
</data>
Run Code Online (Sandbox Code Playgroud)

我知道如何在Clojure中解析它

(require '[clojure.xml :as xml])
(def x (xml/parse 'location/of/that/xml'))
Run Code Online (Sandbox Code Playgroud)

这将返回描述xml的嵌套映射

{:tag :data,
 :attrs nil,
 :content [
     {:tag :products,
      :attrs nil,
      :content [
          {:tag :product,
           :attrs nil,
           :content [] ..
Run Code Online (Sandbox Code Playgroud)

这个结构当然可以用标准的Clojure函数遍历,但它可能会变得非常冗长,特别是与例如用XPath查询它时相比.是否有任何助手可以遍历和搜索这样的结构?例如,我怎么能

  • 得到所有的清单 <product>
  • 只获取<images>标签包含<image>文本"img2.jpg"的产品
  • 得到section"红色部分"的产品

谢谢

pon*_*zao 9

data.zip中使用Zippers是第二个用例的解决方案:

(ns core
  (:use clojure.data.zip.xml)
  (:require [clojure.zip :as zip]
            [clojure.xml :as xml]))

(def data (zip/xml-zip (xml/parse PATH)))
(def products (xml-> data :products :product))

(for [product products :let [image (xml-> product :images :image)]
                       :when (some (text= "img2.jpg") image)]
  {:section (xml1-> product :section text)
   :images (map text image)})
=> ({:section "Red Section", :images ("img.jpg" "img2.jpg")}
    {:section "Green Section", :images ("img.jpg" "img2.jpg")})
Run Code Online (Sandbox Code Playgroud)


小智 5

这是使用data.zip的替代版本,适用于所有三个用例。我发现了这一点,xml->并且xml1->内置了非常强大的导航,在向量中带有子查询。

;; [org.clojure/data.zip "0.1.1"]

(ns example.core
  (:require
   [clojure.zip :as zip]
   [clojure.xml :as xml]
   [clojure.data.zip.xml :refer [text xml-> xml1->]]))

(def data (zip/xml-zip (xml/parse "/tmp/products.xml")))

(let [all-products (xml-> data :products :product)
      red-section (xml1-> data :products :product [:section "Red Section"])
      img2 (xml-> data :products :product [:images [:image "img2.jpg"]])]
  {:all-products (map (fn [product] (xml1-> product :section text)) all-products)
   :red-section (xml1-> red-section :section text)
   :img2 (map (fn [product] (xml1-> product :section text)) img2)})

=> {:all-products ("Red Section" "Blue Section" "Green Section"),
    :red-section "Red Section",
    :img2 ("Red Section" "Green Section")}
Run Code Online (Sandbox Code Playgroud)


Ank*_*kur 3

您可以使用类似的库clj-xpath