问题
I need to transform all characters | to tags in all texts blocks of a big XML file. That is, whenever I found
<test att="one|two">content | something more | and done</test>
I need to transform to
<test att="one|two">content <bar/> something more <bar/> and done</test>
Note that | can also occur in attributes values and, in that case, they must be keeped unchanged. After reading the Transforming slide of the SAX Overview part of the CXML focumentation, I wrote
(defclass preproc (cxml:sax-proxy) ())
(defmethod sax:characters ((handler preproc) data)
(call-next-method handler (cl-ppcre:regex-replace "\\|" data "<bar/>")))
But of course, it produces a string (escaped) not a tag in the final XML.
WML> (cxml:parse "<test>content | ola</test>"
(make-instance 'preproc
:chained-handler (cxml:make-string-sink)))
<?xml version="1.0" encoding="UTF-8"?>
<test>content <bar/> ola</test>"
Any idea or directions?
回答1:
The handler doesn't call the parser, but is handling already parsed values. So, rather than constructing a string that contains <bar/>, what you want to do is to call the method that would have been called if <bar/> had actually been encountered. In this case, if the document had actually had
content <bar/> ola
inside the test element, then there would have been the calls:
(sax:characters handler "content ")
(sax:start-element handler nil nil "bar" '())
(sax:end-element handler nil nil "bar"
(sax:characters handler " ola")
So, all you need to do is split the string on the | character (you can use CL-PPCRE for this if you want, though there may be more lightweight solutions), and then do a call-next-method for each string part, and do calls to sax:start-element and sax:end-element in between:
(defmethod sax:characters ((handler preproc) data)
(let ((parts (cl-ppcre:split "\\|" data)))
;; check this on edge cases, though, e.g., "", "|", "a|", strings
;; without any "|", etc.
(call-next-method handler (pop parts))
(dolist (part parts)
(sax:start-element handler nil nil "bar" '())
(sax:end-element handler nil nil "bar")
(call-next-method handler part))))
(cxml:parse "<test>content | ola</test>"
(make-instance 'preproc
:chained-handler (cxml:make-string-sink)))
;=>
; "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
; <test>content <bar/> ola</test>"
来源:https://stackoverflow.com/questions/26714660/how-to-inject-elements-into-character-content-with-closure-xml