From 09297170939fa2502cb7f62e5f6c8d97f1a5b731 Mon Sep 17 00:00:00 2001 From: Carlo Sciolla Date: Tue, 27 Mar 2012 22:24:45 +0200 Subject: [PATCH 1/3] Add support for namespaces both ways (parse/emit) --- src/main/clojure/clojure/data/xml.clj | 57 ++++++++++++++----- .../clojure/clojure/data/xml/test_namespaces.clj | 14 +++++ 2 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 src/test/clojure/clojure/data/xml/test_namespaces.clj diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 751dc4b..bd846b5 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -13,6 +13,7 @@ (:require [clojure.string :as str]) (:import (javax.xml.stream XMLInputFactory XMLStreamReader + XMLStreamWriter XMLStreamConstants) (java.nio.charset Charset) (java.io Reader))) @@ -27,21 +28,33 @@ (defprotocol Emit (emit-element [element writer])) -(defn write-attributes [{:keys (attrs)} ^javax.xml.stream.XMLStreamWriter writer] +(defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] (doseq [[k v] attrs] - (if (namespace k) - (.writeAttribute writer (str (namespace k)) (name k) (str v)) - (.writeAttribute writer (name k) (str v))))) + (cond + (= "xmlns" (namespace k)) (.writeNamespace writer (name k) v) + (namespace k) (.writeAttribute writer (str (namespace k)) (name k) (str v)) + :else (.writeAttribute writer (name k) (str v))))) + +(defn ns-decls [elem] + (split-with (fn [[k v]] + (= "xmlns" (namespace k))) + (:attrs elem))) ; Represents a node of an XML tree (defrecord Element [tag attrs content] Emit (emit-element [e writer] - (let [nspace (namespace (:tag e)) - qname (name (:tag e)) - ^javax.xml.stream.XMLStreamWriter writer writer] - (.writeStartElement writer "" qname (or nspace "")) - (write-attributes e writer) + (let [[prefix tag] [(namespace (:tag e)) (name (:tag e))] + [nss attrs] (ns-decls e) + uri (and prefix (or (-> (.getNamespaceContext writer) + (.getNamespaceURI prefix)) + ((into {} nss) (keyword "xmlns" prefix)) + "")) + writer ^XMLStreamWriter writer] + (if prefix + (.writeStartElement writer prefix tag uri) + (.writeStartElement writer tag)) + (write-attributes (:attrs e) writer) (doseq [c (:content e)] (emit-element c writer)) (.writeEndElement writer)))) @@ -185,10 +198,24 @@ (when-not (str/blank? p) p))) -(defn- attr-hash [^XMLStreamReader sreader] (into {} - (for [i (range (.getAttributeCount sreader))] - [(keyword (attr-prefix sreader i) (.getAttributeLocalName sreader i)) - (.getAttributeValue sreader i)]))) +(defn- attr-hash [^XMLStreamReader sreader] + (let [attrs (for [i (range (.getAttributeCount sreader))] + [(keyword (attr-prefix sreader i) (.getAttributeLocalName sreader i)) + (.getAttributeValue sreader i)]) + ns-attrs (for [i (range (.getNamespaceCount sreader))] + [(keyword "xmlns" (.getNamespacePrefix sreader i)) + (.getNamespaceURI sreader i)])] + (-> {} + (into attrs) + (into ns-attrs)))) + +(defn- tag-name + [^XMLStreamReader sreader] + (let [name (.getLocalName sreader)] + (let [prefix (.getPrefix sreader)] + (if (not (str/blank? prefix)) + (keyword prefix name) + (keyword name))))) ; Note, sreader is mutable and mutated here in pull-seq, but it's ; protected by a lazy-seq so it's thread-safe. @@ -201,12 +228,12 @@ (condp == (.next sreader) XMLStreamConstants/START_ELEMENT (cons (event :start-element - (keyword (.getLocalName sreader)) + (tag-name sreader) (attr-hash sreader) nil) (pull-seq sreader)) XMLStreamConstants/END_ELEMENT (cons (event :end-element - (keyword (.getLocalName sreader)) nil nil) + (tag-name sreader) nil nil) (pull-seq sreader)) XMLStreamConstants/CHARACTERS (if-let [text (and (not (.isWhiteSpace sreader)) diff --git a/src/test/clojure/clojure/data/xml/test_namespaces.clj b/src/test/clojure/clojure/data/xml/test_namespaces.clj new file mode 100644 index 0000000..e89a2e5 --- /dev/null +++ b/src/test/clojure/clojure/data/xml/test_namespaces.clj @@ -0,0 +1,14 @@ +(ns clojure.data.xml.test-namespaces + (:use [clojure.test :only [deftest is are]] + [clojure.data.xml :as xml :only [element parse-str]])) + +(deftest parse-namespace + (let [input ""] + (is (= (xml/element :app/foo {:xmlns/app "bar"}) + (xml/parse-str input))))) + +(deftest emit-namespace + (is (= (str "" + "") + (xml/emit-str (xml/element :app/foo + {:xmlns/app "bar"}))))) -- 1.7.5.4 From e45634f2d29d05a616d22ce01afcf4038dff3167 Mon Sep 17 00:00:00 2001 From: Carlo Sciolla Date: Tue, 27 Mar 2012 22:31:44 +0200 Subject: [PATCH 2/3] Type hint before usage --- src/main/clojure/clojure/data/xml.clj | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index bd846b5..498e0f1 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -44,13 +44,13 @@ (defrecord Element [tag attrs content] Emit (emit-element [e writer] - (let [[prefix tag] [(namespace (:tag e)) (name (:tag e))] + (let [writer ^XMLStreamWriter writer + [prefix tag] [(namespace (:tag e)) (name (:tag e))] [nss attrs] (ns-decls e) uri (and prefix (or (-> (.getNamespaceContext writer) (.getNamespaceURI prefix)) ((into {} nss) (keyword "xmlns" prefix)) - "")) - writer ^XMLStreamWriter writer] + ""))] (if prefix (.writeStartElement writer prefix tag uri) (.writeStartElement writer tag)) -- 1.7.5.4 From ed4cd06dd0d3fd6ef863c6e790552699e395d620 Mon Sep 17 00:00:00 2001 From: Carlo Sciolla Date: Tue, 27 Mar 2012 22:35:11 +0200 Subject: [PATCH 3/3] Imported class doesn't need explicit package in type hint --- src/main/clojure/clojure/data/xml.clj | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/clojure/clojure/data/xml.clj b/src/main/clojure/clojure/data/xml.clj index 498e0f1..c0ff1e6 100644 --- a/src/main/clojure/clojure/data/xml.clj +++ b/src/main/clojure/clojure/data/xml.clj @@ -28,7 +28,7 @@ (defprotocol Emit (emit-element [element writer])) -(defn write-attributes [attrs ^javax.xml.stream.XMLStreamWriter writer] +(defn write-attributes [attrs ^XMLStreamWriter writer] (doseq [[k v] attrs] (cond (= "xmlns" (namespace k)) (.writeNamespace writer (name k) v) @@ -62,17 +62,17 @@ (defrecord CData [content] Emit (emit-element [e writer] - (.writeCData ^javax.xml.stream.XMLStreamWriter writer (:content e)))) + (.writeCData ^XMLStreamWriter writer (:content e)))) (defrecord Comment [content] Emit (emit-element [e writer] - (.writeComment ^javax.xml.stream.XMLStreamWriter writer (:content e)))) + (.writeComment ^XMLStreamWriter writer (:content e)))) (extend-protocol Emit String (emit-element [e writer] - (.writeCharacters ^javax.xml.stream.XMLStreamWriter writer e))) + (.writeCharacters ^XMLStreamWriter writer e))) (defn element [tag & [attrs & content]] (Element. tag (or attrs {}) (remove nil? content))) -- 1.7.5.4