2012-07-18 28 views
10

Tôi đã xml mẫu sau:Tìm kiếm xml trong Clojure

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

Tôi biết làm thế nào để phân tích nó trong Clojure

(require '[clojure.xml :as xml]) 
(def x (xml/parse 'location/of/that/xml')) 

này trả về một bản đồ lồng nhau mô tả xml

{:tag :data, 
:attrs nil, 
:content [ 
    {:tag :products, 
     :attrs nil, 
     :content [ 
      {:tag :product, 
      :attrs nil, 
      :content [] .. 

Cấu trúc này tất nhiên có thể được đi qua với các hàm Clojure chuẩn, nhưng nó có thể trở nên thực sự tiết, đặc biệt nếu so sánh t o, ví dụ, truy vấn nó bằng XPath. Có người trợ giúp nào để đi qua và tìm kiếm cấu trúc như vậy không? Làm thế nào tôi có thể, ví dụ

  • có được một danh sách tất cả <product>
  • chỉ nhận được các sản phẩm có <images> thẻ chứa một <image> với văn bản "img2.jpg"
  • được những sản phẩm mà section là "Mục Red "

Cảm ơn

Trả lời

3

Bạn có thể sử dụng một thư viện như clj-xpath

012.
+1

Bạn có sẵn sàng chỉnh sửa câu trả lời của mình và thêm ví dụ không? – octopusgrabbus

9

Sử dụng Zippers từ data.zip đây là một giải pháp đối với trường hợp sử dụng thứ hai của bạn:

(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")}) 
0

trong nhiều trường hợp vĩ mô thread-đầu tiên cùng với bản đồ clojures và ngữ nghĩa véc tơ là một cú pháp đầy đủ để truy cập xml. Có rất nhiều trường hợp bạn muốn một cái gì đó cụ thể hơn cho xml (như một thư viện xpath) mặc dù trong nhiều trường hợp ngôn ngữ hiện tại gần như súc tích với việc thêm bất kỳ phụ thuộc nào.

(pprint (-> (xml/parse "/tmp/xml") 
     :content first :content second :content first :content first)) 
"Blue Section" 
3

Đây là phiên bản thay thế sử dụng data.zip, cho cả ba lần sử dụng. Tôi đã tìm thấy rằng xml->xml1-> có điều hướng khá mạnh mẽ được tích hợp sẵn, với các truy vấn phụ trong vec-tơ.

;; [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")} 
+0

+1 Tôi biết bạn đã trả lời sau nhưng bạn có câu trả lời duy nhất cho tất cả 3 câu hỏi và bạn độc đáo tách riêng việc điều hướng và báo cáo kết quả –

1

The Tupelo library có thể dễ dàng giải quyết vấn đề như sử dụng cấu trúc dữ liệu tupelo.forest cây này. Vui lòng see this question for more information. Tài liệu API can be found here.

Tại đây, chúng tôi tải dữ liệu xml của bạn và chuyển đổi dữ liệu đầu tiên thành dạng mở rộng và sau đó là cấu trúc cây bản địa được sử dụng bởi tupelo.forest. Libs & dữ liệu def:

(ns tst.tupelo.forest-examples 
    (:use tupelo.forest tupelo.test) 
    (:require 
    [clojure.data.xml :as dx] 
    [clojure.java.io :as io] 
    [clojure.set :as cs] 
    [net.cgrand.enlive-html :as en-html] 
    [schema.core :as s] 
    [tupelo.core :as t] 
    [tupelo.string :as ts])) 
(t/refer-tupelo) 

(def xml-str-prod "<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> ") 

và khởi mã:

(dotest 
    (with-forest (new-forest) 
    (let [enlive-tree   (->> xml-str-prod 
           java.io.StringReader. 
           en-html/html-resource 
           first) 
      root-hid    (add-tree-enlive enlive-tree) 
      tree-1    (hid->hiccup root-hid) 

Các hậu tố HID viết tắt của "Hex ID", đó là giá trị hex duy nhất mà hoạt động như một con trỏ đến một node/lá trong cây .Ở giai đoạn này chúng ta vừa nạp dữ liệu trong cấu trúc dữ liệu rừng, tạo cây-1 trông giống như:

[:data 
[:tupelo.forest/raw "\n     "] 
[:products 
    [:tupelo.forest/raw "\n      "] 
    [:product 
    [:tupelo.forest/raw "\n      "] 
    [:section "Red Section"] 
    [:tupelo.forest/raw "\n      "] 
    [:images 
    [:tupelo.forest/raw "\n       "] 
    [:image "img.jpg"] 
    [:tupelo.forest/raw "\n       "] 
    [:image "img2.jpg"] 
    [:tupelo.forest/raw "\n      "]] 
    [:tupelo.forest/raw "\n      "]] 
    [:tupelo.forest/raw "\n      "] 
    [:product 
    [:tupelo.forest/raw "\n      "] 
    [:section "Blue Section"] 
    [:tupelo.forest/raw "\n      "] 
    [:images 
    [:tupelo.forest/raw "\n       "] 
    [:image "img.jpg"] 
    [:tupelo.forest/raw "\n       "] 
    [:image "img3.jpg"] 
    [:tupelo.forest/raw "\n      "]] 
    [:tupelo.forest/raw "\n      "]] 
    [:tupelo.forest/raw "\n      "] 
    [:product 
    [:tupelo.forest/raw "\n      "] 
    [:section "Green Section"] 
    [:tupelo.forest/raw "\n      "] 
    [:images 
    [:tupelo.forest/raw "\n       "] 
    [:image "img.jpg"] 
    [:tupelo.forest/raw "\n       "] 
    [:image "img2.jpg"] 
    [:tupelo.forest/raw "\n      "]] 
    [:tupelo.forest/raw "\n      "]] 
    [:tupelo.forest/raw "\n     "]] 
[:tupelo.forest/raw "\n     "]] 

Tiếp theo chúng ta loại bỏ bất kỳ chuỗi trống với mã này:

blank-leaf-hid?  (fn [hid] (and (leaf-hid? hid) ; ensure it is a leaf node 
           (let [value (hid->value hid)] 
             (and (string? value) 
             (or (zero? (count value)) ; empty string 
              (ts/whitespace? value)))))) ; all whitespace string 

blank-leaf-hids  (keep-if blank-leaf-hid? (all-hids)) 
>>     (apply remove-hid blank-leaf-hids) 
tree-2    (hid->hiccup root-hid) 

để sản xuất một đẹp hơn nhiều cây kết quả (định dạng nấc cục)

[:data 
[:products 
    [:product 
    [:section "Red Section"] 
    [:images [:image "img.jpg"] [:image "img2.jpg"]]] 
    [:product 
    [:section "Blue Section"] 
    [:images [:image "img.jpg"] [:image "img3.jpg"]]] 
    [:product 
    [:section "Green Section"] 
    [:images [:image "img.jpg"] [:image "img2.jpg"]]]]] 

Đoạn code sau sẽ tính toán câu trả lời cho ba câu hỏi trên:

product-hids   (find-hids root-hid [:** :product]) 
product-trees-hiccup (mapv hid->hiccup product-hids) 

img2-paths   (find-paths-leaf root-hid [:data :products :product :images :image] "img2.jpg") 
img2-prod-paths  (mapv #(drop-last 2 %) img2-paths) 
img2-prod-hids  (mapv last img2-prod-paths) 
img2-trees-hiccup (mapv hid->hiccup img2-prod-hids) 

red-sect-paths  (find-paths-leaf root-hid [:data :products :product :section] "Red Section") 
red-prod-paths  (mapv #(drop-last 1 %) red-sect-paths) 
red-prod-hids  (mapv last red-prod-paths) 
red-trees-hiccup  (mapv hid->hiccup red-prod-hids)] 

với kết quả:

(is= product-trees-hiccup 
    [[:product 
    [:section "Red Section"] 
    [:images 
     [:image "img.jpg"] 
     [:image "img2.jpg"]]] 
    [:product 
    [:section "Blue Section"] 
    [:images 
     [:image "img.jpg"] 
     [:image "img3.jpg"]]] 
    [:product 
    [:section "Green Section"] 
    [:images 
     [:image "img.jpg"] 
     [:image "img2.jpg"]]]]) 

(is= img2-trees-hiccup 
    [[:product 
    [:section "Red Section"] 
    [:images 
    [:image "img.jpg"] 
    [:image "img2.jpg"]]] 
    [:product 
    [:section "Green Section"] 
    [:images 
    [:image "img.jpg"] 
    [:image "img2.jpg"]]]]) 

(is= red-trees-hiccup 
    [[:product 
    [:section "Red Section"] 
    [:images 
    [:image "img.jpg"] 
    [:image "img2.jpg"]]]])))) 

Các ví dụ đầy đủ có thể được tìm thấy in the forest-examples unit test.

Các vấn đề liên quan