XSLT 从多个模板匹配中写入相同的属性

XSLT write to same attribute from multiple template matches

我 XML 使用了一些自定义属性,但是当往返标准输出(在本例中为 DITA)时,我需要将多个自定义属性的值推送到同一个输出属性.这些值可能来自与元素本身或其祖先之一相匹配的模板。

这是一个简化的例子:

<concept>
    <title>Test concept</test>
    <conbody>
        <p>Info valid for everything and everyone</p>
        <p brand="product1">Some product-specific info here</p>
        <p brand="product2" country="NL">Even more specific info</p>
        <p country="NL">And this is merely localised stuff</p>
    </conbody>
</concept>

我的转换结果应该是有效的 DITA,如下所示:

<concept>
    <title>Test concept</test>
    <conbody>
        <p>Info valid for everything and everyone</p>
        <p props="brand(product1)">Some product-specific info here</p>
        <p props="brand(product2) country(NL)">Even more specific info</p>
        <p props="country(NL)">And this is merely localised stuff</p>
    </conbody>
</concept>

当我尝试从各种模板匹配项(每个模板匹配任何节点上的一个属性)写入相同的属性时,输出中只出现一个值。我可以修改模板以使用两遍,但在某些情况下我需要三遍,甚至更多,这使得它变得非常复杂。创建更具体的模板,在存在一个、两个或多个属性时触发,这些属性需要快速组合成一个模板。

最简单的方法是向另一个模板创建的属性添加一个值,但我不确定在 XSLT 2.0 中是否完全可以这样做 - 因为一个转换的结果不适用于另一个。我还不够专业,不知道是否存在任何可以帮助我创建可维护的 XSL 的东西。如果没有这个选项,我想我最终还是要创建一个相当复杂的多通道模板。

感谢您的肯定回答 ("yes, you can do it and here is how") 或否定回答 ("no, this cannot be done in XSL 2.0")。

调整您的心智模型,避免将多个模板写入单个属性值。相反,考虑您希望属性值是什么,以及如何从 XSLT 中的单个位置的输入 XML 的各个部分收集属性值的片段.

你的大部分输出应该与你的输入相同,所以从身份转换开始。然后为 p 编写一个特例模板,您可以在其中通过遍历输入 p 元素的属性来创建其特殊的 @props 属性。

这是基础知识...

根据您的 XML 输入,

<concept>
    <title>Test concept</title>
    <conbody>
        <p>Info valid for everything and everyone</p>
        <p brand="product1">Some product-specific info here</p>
        <p brand="product2" country="NL">Even more specific info</p>
        <p country="NL">And this is merely localised stuff</p>
    </conbody>
</concept>

此 XSLT 1.0(或 2.0)转换,

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

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

  <xsl:template match="p[@*]">
    <xsl:copy>
      <xsl:attribute name="props">
        <xsl:for-each select="@*">
          <xsl:value-of select="name()"/>
          <xsl:text>(</xsl:text>
          <xsl:value-of select="."/>
          <xsl:text>)</xsl:text>
          <xsl:if test="position() != last()">
            <xsl:text> </xsl:text>
          </xsl:if>
        </xsl:for-each>
      </xsl:attribute>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

将产生此 XML 输出,

<?xml version="1.0" encoding="UTF-8"?>
<concept>
    <title>Test concept</title>
    <conbody>
        <p>Info valid for everything and everyone</p>
        <p props="brand(product1)">Some product-specific info here</p>
        <p props="brand(product2) country(NL)">Even more specific info</p>
        <p props="country(NL)">And this is merely localised stuff</p>
    </conbody>
</concept>

根据要求。

是的,这是可能的,请参阅以下可能的解决方案:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

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

    <xsl:template match="*[@brand or @country]">
        <xsl:copy>
            <xsl:variable name="props.value">
                <!-- Complete the list of profiling attributes -->
                <xsl:for-each select="@brand | @ country">
                    <xsl:value-of select="name()"/><xsl:text>(</xsl:text><xsl:value-of select="."/><xsl:text>)</xsl:text><xsl:text> </xsl:text>
                </xsl:for-each>
            </xsl:variable>
            <xsl:attribute name="props" select="normalize-space($props.value)"></xsl:attribute>
            <!-- TODO : process non-profiling attributes here... -->
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

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

在这里查看它的工作原理:http://xsltransform.net/bnnZWJ


旁注:我的回答与@kjhughes 的不同之处在于我只 select 并处理与 DITA 分析相关的属性 - 您不需要添加 id 或其他任何内容props 属性。