Как разобрать XML с помощью cxml и stp, содержащих амперсанд
-
12-12-2019 - |
Вопрос
Я хочу проанализировать следующий XML-код:
(cxml:parse "<BEGIN><URL>www.some.de/url?some=data&bad=stuff</URL></BEGIN>" (stp:make-builder))
это приводит к
#<CXML:WELL-FORMEDNESS-VIOLATION "~A" {1003C5E163}>
поскольку '&' - это специальный символ XML.Но если я использую &?
вместо этого результатом является:
(cxml:parse "<BEGIN><URL>www.some.de/url?some=data&bad=stuff</URL></BEGIN>" (stp:make-builder))
=>#.(CXML-STP-IMPL::DOCUMENT
:CHILDREN '(#.(CXML-STP:ELEMENT
#| :PARENT of type DOCUMENT |#
:CHILDREN '(#.(CXML-STP:ELEMENT
#| :PARENT of type ELEMENT |#
:CHILDREN '(#.(CXML-STP:TEXT
#| :PARENT of type ELEMENT |#
:DATA "www.some.de/url?some=data")
#.(CXML-STP:TEXT
#| :PARENT of type ELEMENT |#
:DATA "&")
#.(CXML-STP:TEXT
#| :PARENT of type ELEMENT |#
:DATA "bad=stuff"))
:LOCAL-NAME "URL"))
:LOCAL-NAME "BEGIN")))
Что не совсем то, чего я ожидал, поскольку должен быть только один дочерний элемент CXML-STP:TEXT с ДАННЫМИ "www.some.de/url?some=data&bad=stuff"
Как я могу исправить это неправильное (?) поведение?
Решение
Такое поведение, хотя и не очень удобное, на самом деле присутствует и во многих других XML-анализаторах.Вероятно, причина этого заключается в том, чтобы иметь возможность анализировать произвольные XML-объекты и применять к ним некоторые пользовательские правила.Хотя, это может быть просто побочным продуктом реализации синтаксического анализатора.Я пока не мог этого выяснить.
Для варианта синтаксического анализатора SAX я пришел к следующему подходу:
(defclass my-sax (sax:sax-parser-mixin)
((title :accessor title :initform nil)
(tag :accessor tag :initform nil)
(text :accessor text :initform "")))
(defmethod sax:start-element ((sax my-sax) namespace-uri local-name
qname attributes)
(with-slots (tag tagcount text) sax
(setf tag local-name
text "")))
(defmethod sax:characters ((sax my-sax) data)
(with-slots (title tag text) sax
(switch (tag :test 'string=)
("text" (setf text (conatenate 'string text data)))
("title" (setf title data)))))
(defmethod sax:end-element ((sax my-sax) namespace-uri local-name qname)
(with-slots (title tag text) sax
(when (string= "text" local-name)
;; process (text sax)
)))
Т.е.Я собираю текст в sax:characters
и обработать это в sax:end-element
.В STP вы, вероятно, можете обойтись еще проще, просто объединив соседние text
элементы.