While SAX events are useful, their generation is sometimes a little annoying, if you attempt to do it manually. So many functions to call! So many attributes to pass!
cxml includes a macro layer centered around CXML:WITH-XML-OUTPUT, which is more structured, and handles the SAX functions for you.
The most important macros and functions are:
|CXML:WITH-XML-OUTPUT (handler) &body body||macro|
|CXML:ATTRIBUTE (name value)||function|
(Others which we won't go into here in detail are: CXML:DOCTYPE, CXML:WITH-NAMESPACE, CXML:WITH-ELEMENT*, and CXML:PROCESSING-INSTRUCTION.)
CL-USER> (cxml:with-xml-output (cxml:make-string-sink) (cxml:with-element "a" (cxml:attribute "a" 1) (cxml:attribute "b" "c") (cxml:text "d & e")))
0: (SAX:START-DOCUMENT CXML::SINK) 0: (SAX:START-ELEMENT CXML::SINK NIL "a" "a" (#S(SAX::STANDARD-ATTRIBUTE) #S(SAX::STANDARD-ATTRIBUTE))) 0: (SAX:CHARACTERS CXML::SINK "t") 0: (SAX:END-ELEMENT CXML::SINK NIL "a" "a") 0: (SAX:END-DOCUMENT CXML::SINK) "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <a a=\"1\" b=\"c\">d & e</a>"
Points to take away from this:
- A macro or functional layer on top of SAX makes it easy to generate SAX events.
- The implementation of those macros isn't shown here, but I hope it's clear that the code for that is pretty easy.
- I.e. you can write your own convenience layer if you like, and then use it to serialize XML, etc., all without having to actually work with XML serialization and escaping issues.
- You can, of course, plug other sinks in there:
CL-USER> (cxml:with-xml-output (make-instance 'upcaser :chained-handler (stp:make-builder)) (cxml:with-element "a" (cxml:attribute "a" 1) (cxml:attribute "b" "c") (cxml:text "d & e")))
#.(CXML-STP-IMPL::DOCUMENT :CHILDREN '(#.(CXML-STP:ELEMENT :ATTRIBUTES '(#.(CXML-STP:ATTRIBUTE) #.(CXML-STP:ATTRIBUTE)) :CHILDREN '(#.(CXML-STP:TEXT :DATA "D & E")) :LOCAL-NAME "a")))