How to inject elements into character content with Closure XML?

狂风中的少年 提交于 2019-12-11 13:36:28

问题


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 &lt;bar/&gt; 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!