【问题标题】:Can I insert a default xi:fallback instance before processing an XInclude?我可以在处理 XInclude 之前插入默认的 xi:fallback 实例吗?
【发布时间】:2016-04-08 11:02:00
【问题描述】:

假设我有一个使用 XIncludes 的源 XML 文档,如下所示:

<?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>

它在 XIncludes 中调用的另外三个 XML 文档如下所示:

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:fallback 元素,则可以防止此故障,如下所示:

<?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:include 处理 XInclude 之前 - 也就是说,添加默认 xi:不存在的回退实例,然后处理 XInclude 就好像那个 xi:fallback 实例已经存在一样?

感谢您提供任何建议。

【问题讨论】:

  • 您使用什么 XSLT 处理器和 XML 解析来 Xinclude 和转换结果?但无论如何,我认为答案将是:在 XSLT 转换查看文档之前完成了新包含。 XInclude 由 XML 解析器完成,它位于 XSLT 处理的上游。

标签: xml xslt xslt-2.0 xinclude


【解决方案1】:

将我的评论扩展到完整答案,因为这是一个有趣的问题!

XSLT 转换不直接对 XML 文档的文本内容进行操作,而是对内容的树状表示(DOM、XDM)进行操作。输入的这种表示或模型由 XML 解析器提供,理论上,它可以完全独立于 XSLT 处理器。

现在重要的一点是:XML 解析器负责执行 XInclusion,而不是 XSLT 处理器。一旦 XSLT 处理器看到文档模型,就无法知道是否发生了 XInclusions。不,据我所知,在单个 XSLT 转换步骤中 XInclude 之前和之后都无法访问文档树。您可以在不同的模式下两次处理相同的输入节点,但您还需要能够从 XSLT 转换中控制 XML 解析器的 XInclude 功能,这是不可能的。

我建议您绕道而行,分两步解决您的问题:编写一个您应用的 XSLT 转换,没有 XInclude(故意在您的 XML IDE 的 XML 解析器首选项中将其关闭,例如 Oxygen 或在命令行上)来修复丢失的后备:

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,这是一个由 Erik Wilde 纯粹用 XSLT 2.0 编写的 XInclude 处理器。通过将 XIPr 样式表导入到您的原始 XSLT 样式表中,您可以首先提供缺少的回退,正如我在上面向您展示的那样,然后是 process the result with mode="xipr"。在这种情况下,您应该通过 IDE 或命令行工具禁用任何其他 XInclude 处理。

你可以这样做(是的,它有点复杂):

首先,指向您的文件的href 属性必须是绝对的,因为 XIPr 处理器的特殊性:

XML 输入

<?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>

在执行此操作之前,您必须关闭 XML 解析器的 XInclude 选项,下载 XIPr 样式表here,打开它并替换第 52 行

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

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

您必须这样做,因为您要求 XIPr 包含一个临时树的中间结果。如果你在这样的树上使用document-uri(/),它会返回一个空序列,不允许作为resolve-uri()的第二个参数。

现在,最后,如果其中一个文件不存在,结果将是

最终的 XML 输出

<?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>

【讨论】:

  • 非常感谢,Mathias - 这是非常有用的信息。我会考虑这两种选择;无论哪种方式,我都需要建立一个组织中其他人可以轻松运行的流程:这可能需要仔细配置 Oxygen 中的转换场景,以及清晰的文档。
猜你喜欢
  • 1970-01-01
  • 2020-11-20
  • 2017-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多