Разбор XML и получение дерева DOM без привязки пространств имен - Java - PullRequest
2 голосов
/ 07 сентября 2011

У меня есть XML-подобный файл:

<p>something</p>
<ac:image>
    <ri:attachment ri:filename="IMAGE.PNG" />
</ac:image>
<ac:macro ac:name="screenshot">
    <ac:default-parameter>IMAGE.ss</ac:default-parameter>
</ac:macro>
<p>something</p>

Мне нужно преобразовать его с помощью шаблона XSLT - я хочу заменить все <ac:image> на <ac:macro ac:name="screenshot">. Как правило, очень легко анализировать и преобразовывать хорошо сформированные и хорошо известные XML-файлы. Мой случай довольно отличается.

Как видите, у него нет корневого элемента и пролога XML. Но это не проблема, я могу добавить <?xml version="1.0"?> и обернуть содержимое любым произвольным элементом, таким как <root>, чтобы избежать исключения:

Caused by: org.jdom.input.JDOMParseException: Error on line 1: Content is not allowed in prolog.

Пример XML содержит три пространства имен - по умолчанию, ac и ri. Поскольку код будет выполняться на указанном пользователем контенте, могут быть некоторые другие пространства имен, о которых я не знаю. Я не могу связать все пространства имен перед синтаксическим анализом XML, поэтому я сталкиваюсь с исключением:

Caused by: org.xml.sax.SAXParseException: Content is not allowed in prolog.

Где-то в Интернете я обнаружил, что SAX-анализатор может анализировать XML-файлы в режиме, где он не разрешает пространства имен. В режиме по умолчанию вы получаете namespace=ac и element=macro, тогда как в режиме без пространства имен вы не получаете пространство имен и element=ac:macro. И это желательно. Все, что вам нужно, это установить функции SAX в парсере: namespaces=false, namespace-prefixes=true.

final XMLReader sax = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
sax.setFeature("http://xml.org/sax/features/validation", false);
sax.setFeature("http://xml.org/sax/features/namespaces", false);
sax.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
sax.parse(new InputSource(new StringReader(content))); // parse returns void

Он не выдает никаких исключений, поэтому похоже, что XML анализируется без ошибки. Однако мне нужно дерево DOM, чтобы я мог преобразовать его с помощью XSLT. Давайте использовать JDOM тогда:

// all classes are org.jdom.*
final SAXBuilder sax = new SAXBuilder(false); // validate=false
sax.setFeature("http://xml.org/sax/features/namespaces", false);
sax.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
final Document document = sax.build(new StringInputStream(content));

К сожалению, я получаю исключение:

Caused by: org.jdom.IllegalNameException: The name "" is not legal for JDOM/XML elements: XML names cannot be null or empty.
    at org.jdom.Element.setName(Element.java:206)
    at org.jdom.Element.<init>(Element.java:140)
    at org.jdom.Element.<init>(Element.java:152)
    at org.jdom.DefaultJDOMFactory.element(DefaultJDOMFactory.java:138)
    at org.jdom.input.SAXHandler.startElement(SAXHandler.java:511)
    at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
    at org.apache.xerces.impl.dtd.XMLDTDValidator.startElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentScannerImpl$ContentDispatcher.scanRootElementHook(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:453)
    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:770)
    at com.screensnipe.confluence.macro.XhtmlImageMacroReplacer.replaceImageMacroInText(XhtmlImageMacroReplacer.java:118)

JDOM жалуется на недопустимое имя тега <>. Конечно, у меня нет такого. Похоже, что у JDOM есть ошибка в SAXHandler.java:511, element = factory.element(localName); должно быть element = factory.element(qName);.

Я также попробовал XOM. XOM не работает с функцией «пространства имен», для которой установлено значение false .

Я также попробовал библиотеку TagSoup. Мне не нравится это, потому что это портит выходной XML. Добавление пролога XML и корневого элемента не является проблемой. Возиться с пространствами имен можно.

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
    <body>
        <p>something</p>
        <ac:image xmlns:ac="urn:x-prefix:ac"> <!-- :( -->
             <ri:attachment xmlns:ri="urn:x-prefix:ri" ri:filename="IMAGE.PNG" />
        </ac:image>
        ...

Вопрос: Как получить дерево DOM из моего XML? (Java) Без написания моей версии JDOM. Буду признателен за рабочее решение. Просто проанализируйте и получите дерево DOM. Дерево, в котором пространства имен не нарушены, как в библиотеке TagSoup.

Или более целенаправленный вопрос: как заменить <ac:image> на <ac:macro ac:name="screenshot">, не касаясь других тегов? (Java) Все остальные теги, пространства имен или что-либо еще должны быть неизменными. (Не предлагайте регулярные выражения)

1 Ответ

1 голос
/ 07 сентября 2011

Если вы хотите выполнить предварительную обработку, например, добавление окружающего корневого элемента, вы можете также просмотреть XML-файл на предмет префиксов пространства имен и добавить фиктивные объявления для каждого из них в добавляемый корневой элемент..

Тогда вам не понадобится синтаксический анализатор, которому можно будет сказать, что он не разрешает префиксы пространства имен.

...