У меня есть 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) Все остальные теги, пространства имен или что-либо еще должны быть неизменными. (Не предлагайте регулярные выражения)