Попытка использовать XInclude с Java и разрешение фрагмента с помощью xml:id

Я пытался заставить XInclude работать в моем XML-документе и, наконец, заставить его работать в Oxygen XML, который я использую для создания XML-документов.

Затем я перешел к своему приложению, написанному на Java, но, похоже, оно не поддерживает какую-либо форму разрешения XPointer, кроме использования чего-то вроде: element(/1/2).

Очевидно, что это ужасная схема, которую нужно использовать, поскольку каждый раз, когда документ редактируется, XPointer должен меняться, чтобы отразить новую позицию узла в XML!

Схема, с которой я работал, просто использовала xml:id в целевом документе:

<foo>
    <bar xml:id="ABCD" />
</foo>

а затем в другом документе:

<lorem>
    <ipsum>
         <xi:include href="target.xml" xpointer="ABCD" />
    </ipsum>
</lorem>

Что я ожидаю (и получаю в кислороде) приводит к чему-то вроде:

<lorem>
    <ipsum>
         <bar xml:id="ABCD" />
    </ipsum>
</lorem>.

Тем не менее, в Java это не с:

Ошибка чтения файла в формате XML (href='data/target.xml'). Причина: неудачное разрешение XPointer.

Если, однако, я изменяю тег включения, чтобы использовать

xpointer="element(/1/1)"

тогда это работает очень хорошо - но, как я уже сказал, это очень плохое решение.

Я просто использую реализации, которые включены в среду выполнения Java (1.8).

Вот код, который я использую:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setXIncludeAware(true);
Source resultSource = new 
StreamSource(Gdx.files.internal("data/result.xsd").read());
            Source targetSource = new 
StreamSource(Gdx.files.internal("data/target.xsd").read());
            Source[] schemaFiles = {targetSource, resultSource};
            schema = 
SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema")
                    .newSchema(schemaFiles);
            factory.setSchema(schema);
            builder = factory.newDocumentBuilder();
            itemDoc = builder.parse(new 
InputSource(Gdx.files.internal("data/result.xml").read()));

1 ответ

Решение

Согласно документации Apache Xerces по XInclude (который используется внутри для анализа XML в Java)

для кратких указателей и XPointers element() в настоящее время поддерживаются только идентификаторы DTD.

Это означает, что вы должны поместить объявления разметки, такие как следующие в ваш target.xml файл (сообщающий анализатору XML, что id атрибут должен рассматриваться как атрибут с ID семантика и указание XInclude интерпретировать "голые" XPointers как ссылки на идентификаторы):

<!DOCTYPE foo [
  <!ATTLIST bar id ID #IMPLIED>
]>
<foo>
    <bar id="ABCD"/>
</foo>

Если вы сейчас используете следующий документ в качестве исходного XML (который вы назвали result.xml в вашем примере кода, и который я отредактировал, чтобы содержать привязку URI пространства имен XInclude для xi)

<lorem xmlns:xi="http://www.w3.org/2001/XInclude">
  <ipsum>
    <xi:include href="target.xml" xpointer="ABCD"/>
  </ipsum>
</lorem>

тогда Xerces создаст Document где обработка XInclude была выполнена по желанию (где я поместил данные вашего примера в target.xml файл в том же каталоге, что и result.xml файл):

<lorem xmlns:xi="http://www.w3.org/2001/XInclude">
  <ipsum>
    <bar id="ABCD" xml:base="target.xml"/>
  </ipsum>
</lorem>

Код Java, который я использовал для создания документа, упрощен по сравнению с вашим примером и не содержит сторонних библиотек:

import java.io.*;
import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.validation.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;
import org.w3c.dom.*;

public class t {

  public static void main(String[] args) {
    try {
      DocumentBuilderFactory factory =
        DocumentBuilderFactory.newInstance();
      factory.setNamespaceAware(true);
      factory.setXIncludeAware(true);
      DocumentBuilder builder = factory.newDocumentBuilder();
      Document itemDoc = builder.parse(new File("result.xml"));
      System.out.println(serialize(itemDoc));
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  static String serialize(Document doc) throws Exception {
    Transformer transformer =
      TransformerFactory.newInstance().newTransformer();
    StreamResult result = new StreamResult(new StringWriter());
    DOMSource source = new DOMSource(doc);
    transformer.transform(source, result);
    return result.getWriter().toString();
  }
}

Поскольку вы также используете проверку XML-схемы, я также хотел бы указать на потенциальное взаимодействие XInclude с XML-схемой, например. обсуждается в проверке схемы / пространства имен XInclude?, а также потенциальная альтернатива, обсуждаемая в Duplicate некоторых частях XML без переписывания их.

Другие вопросы по тегам