Skip to content

Instantly share code, notes, and snippets.

@jcromartie jcromartie/xpath.clj
Last active Dec 15, 2015

Embed
What would you like to do?
;; factory factories, etc.
(def dbf (doto (DocumentBuilderFactory/newInstance)
(.setNamespaceAware true)))
(def doc-builder (.newDocumentBuilder dbf))
(defn namespace-map
"Returns an implementation of NamespaceContext ... actual usefulness TBD"
[mapping]
(let [prefixes (fn [uri] (map key (filter #(= uri (val %)) mapping)))]
(proxy [Object NamespaceContext] []
(getNamespaceURI [prefix] (get mapping prefix))
(getPrefix [uri] (first (prefixes uri)))
(getPrefixes [uri] (.iterator (prefixes uri))))))
(defn xpath-instance
[mapping]
(let [instance (.newXPath (XPathFactory/newInstance))]
(if mapping
(doto instance
(.setNamespaceContext (namespace-map mapping)))
instance)))
;; the meat of it
(defn parse
"Returns DOM Document by loading and parsing resource at f"
[f]
(.parse doc-builder (clojure.java.io/input-stream f)))
(defn node-seq
"Returns a seq of the nodes in nodelist"
[^NodeList nodelist]
(map #(.item nodelist %) (range (.getLength nodelist))))
(def ^:dynamic *xpath* nil)
(defmacro with-ns-mappings
[mappings & forms]
`(binding [*xpath* (xpath-instance ~mappings)]
~@forms))
(defn xpath
"Evaluates xpath expression in the context of given node (or doc),
returns seq of nodes that match"
[node expr]
(let [xpath-instance (or *xpath* (xpath-instance nil))
compiled (.compile xpath-instance expr)]
(node-seq (.evaluate compiled node XPathConstants/NODESET))))
(defn node?
[x]
(isa? (class x) Node))
(defn text
"Returns text of XML DOM Node or sequence of nodes"
[node]
(cond
(seq? node) (clojure.string/join " " (map text node))
(node? node) (.getTextContent node)
:else nil))
(defn attr
"Returns node's attr value for key"
[node key]
(when-let [item (.getNamedItem (.getAttributes node) key)]
(.getValue item)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.