Различное поведение трансформатора по умолчанию и идентичной трансформации XSLT

Вопрос или проблема

У меня есть следующий XML:

<?xml version="1.0"?>
<x:books xmlns:x="urn:collection" xmlns:p="urn:property">
  <book id="bk001">
    <p:author>Автор</p:author>
    <p:title>Первая книга</p:title>
    <p:genre>Художественная литература</p:genre>
  </book>
</x:books>

который разбирается с помощью парсера, не учитывающего пространства имен:

DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultInstance(); // реализация по умолчанию JDK
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(new InputSource(new StringReader(xmlString)));

Когда он преобразуется обратно в строку с использованием преобразователя идентичности по умолчанию…

TransformerFactory factory = TransformerFactory.newDefaultInstance(); // реализация по умолчанию JDK
Transformer tr0 = factory.newTransformer();
Writer w0 = new StringWriter();
tr0.transform(new DOMSource(dom), new StreamResult(w0));

…я получаю первоначальный XML без изменений (со всеми префиксами пространств имен x: и p: сохраненными).

Однако, когда я пытаюсь использовать преобразование XSLT идентичности для преобразования его…

<transform version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
    <output method="xml" encoding="UTF-8" indent="no" omit-xml-declaration="no" />
    <template match="*|@*|node()">
        <copy><apply-templates select="*|@*|node()" /></copy>
    </template>
</transform>
Transformer tr1 = factory.newTransformer(new StreamSource(new StringReader(xsltString)));
Writer w1 = new StringWriter();
tr1.transform(new DOMSource(dom), new StreamResult(w1));

… я получаю следующий XML (со всеми префиксами пространств имен удаленными):

<?xml version="1.0" encoding="UTF-8"?><books xmlns:p="urn:property" xmlns:x="urn:collection">
  <book id="bk001">
    <author>Автор</author>
    <title>Первая книга</title>
    <genre>Художественная литература</genre>
  </book>
</books>

Учитывая, что в моей кодовой базе я не могу контролировать, как создается DOM (он может быть с учетом или без учета пространств имен, последний в большинстве случаев), как я могу написать XSLT 1.0 преобразование, которое всегда будет сохранять все префиксы пространств имен XML?
(Преобразование идентичности – это пример, я хотел бы написать более сложные преобразования в будущем)

Существует ли какое-либо свойство, которое я могу установить в TransformerFactory, или какие-либо изменения в атрибуте XSLT match="...", которые я могу сделать для достижения этого?

Ответ или решение

Различие между поведением стандартного трансформатора и трансформацией по идентичности XSLT

При работе с XML-документами, содержащими пространства имен, различия в поведении стандартного трансформатора и XSLT трансформации по идентичности могут вызывать недоразумения, особенно когда дело касается сохранения префиксов пространств имен. Рассмотрим конкретный случай, чтобы прояснить данную ситуацию и рассмотреть способы сохранения префиксов пространств имен с помощью XSLT.

Исходные данные

У вас есть следующий XML-документ:

<?xml version="1.0"?>
<x:books xmlns:x="urn:collection" xmlns:p="urn:property">
  <book id="bk001">
    <p:author>Writer</p:author>
    <p:title>The First Book</p:title>
    <p:genre>Fiction</p:genre>
  </book>
</x:books>

Этот документ разбирается с помощью парсера, не учитывающего пространства имен:

DocumentBuilderFactory factory = DocumentBuilderFactory.newDefaultInstance();
factory.setNamespaceAware(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(new InputSource(new StringReader(xmlString)));

Примеры трансформации

Когда вы используете стандартный идентичный трансформатор:

TransformerFactory factory = TransformerFactory.newDefaultInstance();
Transformer tr0 = factory.newTransformer();
Writer w0 = new StringWriter();
tr0.transform(new DOMSource(dom), new StreamResult(w0));

Результат

В этом случае вы получаете оригинальный XML с сохраненными префиксами пространства имен:

<?xml version="1.0"?>
<x:books xmlns:x="urn:collection" xmlns:p="urn:property">
  <book id="bk001">
    <p:author>Writer</p:author>
    <p:title>The First Book</p:title>
    <p:genre>Fiction</p:genre>
  </book>
</x:books>

Однако, если вы используете XSLT для идентичной трансформации:

<transform version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
    <output method="xml" encoding="UTF-8" indent="no" omit-xml-declaration="no" />
    <template match="*|@*|node()">
        <copy><apply-templates select="*|@*|node()" /></copy>
    </template>
</transform>

При этом результирующий XML будет выглядеть следующим образом (с удаленными префиксами):

<?xml version="1.0" encoding="UTF-8"?>
<books xmlns:p="urn:property" xmlns:x="urn:collection">
  <book id="bk001">
    <author>Writer</author>
    <title>The First Book</title>
    <genre>Fiction</genre>
  </book>
</books>

Причины различий

  1. Непонимание пространств имен: Стандартный трансформатор не учитывает пространство имен при сериализации, тогда как XSLT обрабатывает элементы на основе их имени, что может привести к потере префиксов.

  2. Однообразие пространства имен: В XSLT при создании новых элементов происходит использование "пустого" пространства имен, что и приводит к удалению префиксов.

Решение проблемы

Для того чтобы сохранить префиксы пространств имен в XSLT трансформации, необходимо явно определить их в вашем XSLT. Возможное решение заключается в добавлении пространств имен, как показано ниже:

<transform version="1.0" 
           xmlns="http://www.w3.org/1999/XSL/Transform"
           xmlns:x="urn:collection" 
           xmlns:p="urn:property">
    <output method="xml" encoding="UTF-8" indent="no" omit-xml-declaration="no" />
    <template match="*|@*|node()">
        <copy>
            <xsl:copy-of select="namespace::*"/>
            <apply-templates select="*|@*|node()" />
        </copy>
    </template>
</transform>

В этом случае, добавляя xsl:copy-of select="namespace::*", мы сохраняем все пространства имен, включая префиксы.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...