2016-01-04 2 views
2

Скажем, у меня есть документ источника XML, который использует XIncludes, как это:Могу ли я вставить экземпляр xi: fallback по умолчанию перед обработкой XInclude?

<?xml version="1.0" encoding="UTF-8"?> 
<parent xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="parent01"> 
    <xi:include href="child01.xml"/> 
    <xi:include href="child02.xml"/> 
    <xi:include href="child03.xml"/> 
</parent> 

Три дополнительных документов XML вызываемыми в XIncludes выглядеть следующим образом:

child01.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<children> 
    <child xml:id="child01"> 
     <p>This is child 1.</p> 
    </child> 
</children> 

child02.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<children> 
    <child xml:id="child02"> 
     <p>This is child 2.</p> 
    </child> 
</children> 

child03.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<children> 
    <child xml:id="child03"> 
     <p>This is child 3.</p> 
    </child> 
</children> 

У меня есть XSLT 2.0 преобразовать так:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/> 

    <xsl:strip-space elements="*"/> 

    <xsl:template match="/"> 
     <xsl:apply-templates select="parent"/> 
    </xsl:template> 

    <xsl:template match="parent"> 
     <volume> 
     <xsl:apply-templates select="@*|.//child"/> 
     </volume> 
    </xsl:template> 

    <xsl:template match="child"> 
     <chapter> 
     <xsl:apply-templates select="@*|*|text()"/> 
     </chapter> 
    </xsl:template> 

    <xsl:template match="@*|*|text()"> 
     <xsl:copy copy-namespaces="no"> 
     <xsl:apply-templates select="@*|*|text()"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

Когда все файлы, на которые ссылается XIncludes присутствуют в той же папке, parent01.xml, мой преобразование работает просто отлично, и производит это выход:

<?xml version="1.0" encoding="UTF-8"?> 
<volume xml:id="parent01"> 
    <chapter xml:id="child01"> 
     <p>This is child 1.</p> 
    </chapter> 
    <chapter xml:id="child02"> 
     <p>This is child 2.</p> 
    </chapter> 
    <chapter xml:id="child03"> 
     <p>This is child 3.</p> 
    </chapter> 
</volume> 

Однако, если один файл - скажем, child02.xml - отсутствует, преобразование завершается с ошибкой.

Эта неудача была бы предотвратить, если бы parent01.xml были включены XI: элементы замещающего, как это:

<?xml version="1.0" encoding="UTF-8"?> 
<parent xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="parent01"> 
    <xi:include href="child01.xml"> 
     <xi:fallback> 
     <child> 
      <p>The file is missing.</p> 
     </child> 
     </xi:fallback> 
    </xi:include> 
    <xi:include href="child02.xml"> 
     <xi:fallback> 
     <child> 
      <p>The file is missing.</p> 
     </child> 
     </xi:fallback> 
    </xi:include> 
    <xi:include href="child03.xml"> 
     <xi:fallback> 
     <child> 
      <p>The file is missing.</p> 
     </child> 
     </xi:fallback> 
    </xi:include> 
</parent> 

Затем выходной сигнал был бы следующим образом:

<?xml version="1.0" encoding="UTF-8"?> 
<volume xml:id="parent01"> 
    <chapter xml:id="child01"> 
     <p>This is child 1.</p> 
    </chapter> 
    <chapter> 
     <p>The file is missing.</p> 
    </chapter> 
    <chapter xml:id="child03"> 
     <p>This is child 3.</p> 
    </chapter> 
</volume> 

Мой вопрос это: можно ли написать мой XSLT-преобразование, чтобы вставить экземпляр xi: fallback в каждый xi: включить до, обрабатывая XInclude - то есть, чтобы добавить стандартный xi: резервный экземпляр, где ни один не присутствует, а затем pro вывести XInclude так, как если бы этот xi: резервный экземпляр присутствовал?

Благодарим за любые советы, которые может предложить любой.

+0

Что XSLT процессор и XML разбора вы используете для XInclude и преобразование результат? Но в любом случае, я думаю, что ответ будет следующим: Xinclusions сделаны _before_ преобразование XSLT позволяет увидеть документ. XInclude выполняется парсером XML, который находится выше по потоку от обработки XSLT. –

ответ

1

Расширение комментария к полному ответу, так как это интересный вопрос!

Преобразования XSLT не действуют непосредственно на текстовое содержимое XML-документов, а на древовидное представление содержимого (DOM, XDM). Это представление или модель ввода обеспечивается парсером XML, который теоретически может быть полностью независим от XSLT-процессора.

Теперь важный бит: XML-парсер отвечает за выполнение XInclusions, а не за XSLT-процессор. Как только XSLT-процессор увидит модель документа, нет способа узнать, имели место ли XInclusions. И нет, насколько мне известно, нет способа получить доступ к дереву документов до и после XInclude на одном этапе преобразования XSLT. Вы можете обрабатывать одни и те же входные узлы дважды в другом режиме, но вам также нужно будет управлять функцией XInclude парсера XML из преобразования XSLT, что невозможно.

Я предлагаю вам взять небольшой крюк и решить проблему в два этапа: Напишите преобразование XSLT, который Вы применяете без XInclude (намеренно отключение этой в XML парсер настройках вашего XML IDE, как кислорода или командная строка), чтобы установить недостающие резервные объявления:

XSLT исправить откаты

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xi="http://www.w3.org/2001/XInclude" 
    version="2.0"> 

    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="xi:include[not(xi:fallback)]"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xi:fallback> 
       <child> 
        <p>The file is missing.</p> 
       </child> 
      </xi:fallback> 
      <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

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

<?xml version="1.0" encoding="UTF-8"?> 
<parent xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="parent01"> 
    <xi:include href="child01.xml"> 
     <xi:fallback> 
      <child> 
       <p>The file is missing.</p> 
      </child> 
     </xi:fallback> 
    </xi:include> 
    <xi:include href="child02.xml"> 
     <xi:fallback> 
      <child> 
       <p>The file is missing.</p> 
      </child> 
     </xi:fallback> 
    </xi:include> 
    <xi:include href="child03.xml"> 
     <xi:fallback> 
      <child> 
       <p>The file is missing.</p> 
      </child> 
     </xi:fallback> 
    </xi:include> 
</parent> 

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


Если это неприемлемо для вас, вы можете посмотреть в XIPr, процессор XInclude написанный чисто в XSLT 2.0 Эрик Уайльда. Импортируя таблицы стилей XIPr в свои исходные таблицы стилей XSLT, вы можете сначала предоставить отсутствующие резервные копии, как я показал вам выше, а затем process the result with mode="xipr". В этом случае вы должны отключить любую другую обработку XInclude с помощью средства IDE или командной строки.

Вот как вы можете это сделать (да, это становится своего рода сложным):

Прежде всего, href атрибуты, указывающие на ваши файлы должны быть абсолютными, из-за особенностей го процессора XIPr :

XML Input

<?xml version="1.0" encoding="UTF-8"?> 
<parent xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="parent01"> 
    <xi:include href="file:/Users/User/Desktop/child01.xml"/> 
    <xi:include href="file:/Users/User/Desktop/child02.xml"/> 
    <xi:include href="file:/Users/User/Desktop/child03.xml"/> 
</parent> 

таблицы стилей XSLT

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" 
    xmlns:xi="http://www.w3.org/2001/XInclude" 
    exclude-result-prefixes="xi"> 

    <xsl:import href="xipr.xsl"/> 

    <xsl:output method="xml" encoding="UTF-8" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="/"> 
     <xsl:variable name="fixedfallbacks"> 
      <xsl:apply-templates select="." mode="fixfallbacks"/> 
     </xsl:variable> 
     <xsl:variable name="xincluded"> 
      <xsl:apply-templates select="$fixedfallbacks" mode="xipr"/> 
     </xsl:variable> 
     <xsl:apply-templates select="$xincluded/*" mode="#default"/> 
    </xsl:template> 

    <xsl:template match="xi:include[not(xi:fallback)]" mode="fixfallbacks"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()" mode="fixfallbacks"/> 
      <xi:fallback> 
       <child> 
        <p>The file is missing.</p> 
       </child> 
      </xi:fallback> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@*|node()" mode="fixfallbacks"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()" mode="fixfallbacks"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="parent"> 
     <volume> 
      <xsl:apply-templates select="@*|.//child"/> 
     </volume> 
    </xsl:template> 

    <xsl:template match="child"> 
     <chapter> 
      <xsl:apply-templates select="@*|*|text()"/> 
     </chapter> 
    </xsl:template> 

    <xsl:template match="@*|*|text()"> 
     <xsl:copy copy-namespaces="no"> 
      <xsl:apply-templates select="@*|*|text()"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Перед выполнением этого нужно отключить опцию XInclude вашего XML-парсер, скачать XIPr таблицы стилей here, откройте его и замените строку 52

<xsl:variable name="include-uri" select="resolve-uri(@href, document-uri(/))"/> 

с

<xsl:variable name="include-uri" select="resolve-uri(@href)"/> 

Вы должны сделать это, потому что вы запрашиваете XIPr для XInclude промежуточного результата, который является временным деревом. Если вы используете document-uri(/) на таком дереве, он вернет пустую последовательность, которая недопустима как второй аргумент resolve-uri().

Теперь, наконец, если один из файлов не существует, то результат будет

Final XML Output

<?xml version="1.0" encoding="UTF-8"?> 
<volume xml:id="parent01"> 
    <chapter> 
     <p>The file is missing.</p> 
    </chapter> 
    <chapter xml:id="child02"> 
     <p>This is child 2.</p> 
    </chapter> 
    <chapter xml:id="child03"> 
     <p>This is child 3.</p> 
    </chapter> 
</volume> 
+0

Огромное спасибо, Матиас - это очень полезная информация. Я рассмотрю оба эти варианта; в любом случае, мне нужно настроить процесс, который могут легко выполнить другие люди в организации: это, вероятно, будет вопросом тщательной настройки сценариев трансформации в Oxygen и четкой документацией. – MDow