4

I'm having some issues with clojure.data.xml in that when parsing bad XML, the exception thrown is not caught. I've found some issues perhaps with run-time wrappers, but my attempts to unwrap it have been unsuccessful can anyone point out to me why this may be happening?

(defn parse-xml-from-string
  "takes in valid xml as a string and turns it into 
   #clojure.data.xml data, if bad xml returns false"
  [xml]
  (try
    (do (parse (java.io.StringReader. xml)))
    (catch javax.xml.stream.XMLStreamException e false)
    (catch Exception ex
      (cond (isa? (class (.getCause ex)) javax.xml.stream.XMLStreamException) false))))

method call

(viva-api.helpers.validation/parse-xml-from-string "<?xml version=\"1.0\"encoding=\"UTF-8\"?><foo><bar><baz>The baz value</baz></bar></foos>")

output

#clojure.data.xml.Element{:tag :foo, :attrs {}, :content (user=> XMLStreamException  ParseError at [row,col]:[1,84]
Message: The end-tag for element type "foo" must end with a '>' delimiter.  com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next (XMLStreamReaderImpl.java:598)
2
  • Why are you trying to catch the exception twice? Also the (do) block is not necessary with only one expression inside. Commented Jul 26, 2013 at 18:55
  • edited to show input/output. the reason for the two catches is to show what I had found as possible solutions. Commented Jul 26, 2013 at 19:19

1 Answer 1

3

I think the problem you are seeing is related to the laziness of the value returned by parse. According to its docstring "Parses the source, which can be an InputStream or Reader, and returns a lazy tree of Element records. [...]".

(ns xml
  (:use clojure.data.xml))

(defn parse-xml-from-string
  "takes in valid xml as a string and turns it into 
   #clojure.data.xml data, if bad xml returns false"
  [xml]
  (try
    (parse (java.io.StringReader. xml))
    (catch javax.xml.stream.XMLStreamException ex
      false)))

(parse-xml-from-string "<bla/>") ;= #clojure.data.xml.Element{:tag :bla, :attrs {}, :content ()}

(parse-xml-from-string "<bla") ;= false

(parse-xml-from-string "<bla>") ; throws XMLStreamException

(def x (parse-xml-from-string "<bla>")) ; doesn't throw an exception unless it's evaluated

x ; throws XMLStreamException

EDIT

The value returned from parse is a lazy tree built top-down from an Element record and based on a lazy sequence of Event objects, as mentioned in the docstring for the event-tree function. The laziness lies in the :content field of the record which is realized when the field is accessed. One way I've found to force the realization of the whole tree is using the str function, this feels hacky and looks ugly but anyone who has a better idea can provide a better solution.

(defn parse-xml-from-string
  "takes in valid xml as a string and turns it into 
   #clojure.data.xml data, if bad xml returns false"
  [xml]
  (try
    (let [x (parse-str xml)]
      (str x)
      x)
    (catch javax.xml.stream.XMLStreamException ex
      false)))

This seems like going to great lengths to avoid laziness which is, as I understand it, one of the main reasons to use clojure.data.xml. Since you seem to want your whole XML string parsed at once maybe the clojure.xml/parse function is better suited for your needs.

(defn my-parse-str
  [s]
  (try
    (xml/parse (java.io.ByteArrayInputStream. (.getBytes s)))
    (catch Exception e false)))
Sign up to request clarification or add additional context in comments.

6 Comments

In that case he should replace his do with doall.
with doall I get this output XMLStreamException ParseError at [row,col]:[1,84] Message: The end-tag for element type "foo" must end with a '>' delimiter. com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next (XMLStreamReaderImpl.java:598) #clojure.data.xml.Element{:tag :foo, :attrs {}, :content (user=>
@user1944838 I've tried using doall but it doesn't work, the data structure returned by parse is not a simple lazy sequence.
but it is a lazy tree
@ChadJPetersen Just edited the answer with a possible workaround and some comments.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.