xml.location
2020-03-25
This system provides a convenient interface for manipulating XML data. It is inspired by the xmltio library.
Upstream URL
Author
Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
Maintainer
Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
License
LLGPLv3
xml.location README
1Introduction
Thexml.location system provides manipulation of and a conversionmechanisms for XML data:- Typed, XPath-based location bindings
- Extensible Lisp -> XML and XML -> Lisp conversion
- Creation of XPath-specified XML structures
- Automatic compile-time parsing of XML documents and XPaths
This means, when working with an XML document
<root foo="1 2 3">old text</root>
we can write(let ((document (cxml:parse "<root foo=\"1 2 3\">old text</root>" (stp:make-builder))))
(with-locations (((:name name) "node()")
(text "node()/text()")
((:@ (foo "foo") :type '(list number)) "node()")) document
(setf text "new text"
foo '(4 5))
(values name text foo (stp:serialize document (cxml:make-string-sink)))))
=>
(values
"root"
"new text"
(4 5)
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root foo=\"4 5\">new text</root>")
2Tutorial
The simplest case of usingxml.location looks like this:(let ((loc (xml.location:loc "<foo a='1' b='c d'>bar baz</foo>" "node()")))
(values
(xml.location:name loc)
(xml.location:@ loc "a" :type 'integer)
(xml.location:@ loc "b" :type '(list symbol))))
=> (values "foo" 1 (C D))
The first line uses the xml.location:loc function to construct axml.location:location object for the document ~<foo a="1" b="cd">bar baz</foo>~ and the XPath node(). In lines 3 -5, thefollowing things are extracted and returned as Lisp objects:- the name of the root node (using the
xml.location:nameaccessor) - the value of the attribute "a", interpreted as
integer(usingthexml.location:@attribute accessor) - the value of the attribute "b", interpreted as
listofsymbols
The accessors xml.location:name, xml.location:@ and
xml.location:val are setf able places:
(let ((loc (xml.location:loc "<foo old-attr='1'/>" "node()[@pred-attr='baz']"
:if-no-match :create)))
(setf (xml.location:@ loc "old-attr" :type 'number) 2
(xml.location:@ loc "new-attr") "foo")
loc)
=> #<(LOCATION 3 MIXINS) node()[@pred-attr='baz'] in <foo new-attr="foo" pred-attr="baz" old-attr="2"/> {FAC7D81}>
Note how :if-no-match :create causes specified locations to becreated if they do not exist already --- including things specifiedin form of predicates in some cases.In both previous examples, a single xml.location:location object
was used multiple times. Such cases can be simplified using the
xml.location:with-locations-r/o and xml.location:with-locations
macros. The former binds variables to values extracted from XML
locations while the latter uses symbol macros to make XML locations
setf able places:
(xml.location:with-locations
(((:name name) "node()")
(text "bla/text()")
((:@ (my-foo "foo") :type '(list number)) "node()")
((:@ bar) "node()"))
"<bla foo='1 2 4' bar='baz'>foo</bla>"
;; Set values of generalized variables
(setf name "frooble"
my-foo '(5 6)
bar 42
text "bubba")
;; Extract values from generalized variables
(values name my-foo bar text))
=> (values "frooble" (5 6) "42" "bubba")
3TODONamespaces
4Conversion Infrastructure
The core of the conversion infrastructure consists of twooperations:- Lisp -> XML conversion
- XML -> Lisp conversion
4.1TODOLisp to XML conversion
4.2TODOXML to Lisp conversion
4.3Adding Conversions
There are several possible ways to define to/from XML conversionmethods for a type:- Types that have obvious string representations
- Types that require a structured representation
- Types that require a structured representation and have internalstructure that can be represented in several different ways
read and print which is often sufficient.For the second kind of type, at least the following two methods have to be defined:
(defmethod ->xml ((value MY-TYPE)
(dest stp:element)
(type t))
"Store VALUE in XML element DEST."
;; actual conversion code
)
(defmethod xml-> ((value stp:element)
(type 'MY-TYPE))
"Retrieve an instance of MY-TYPE from the XML element VALUE."
;; actual conversion code
)