通过通用模板和目标模板处理元素



我是XSL的新手,仍然不确定我的一些术语。我目前正面临着一些我似乎无法破解的事情。我正在尝试

  1. 搜索输入 XML 的所有数据节点(叶元素?)并替换文本

  2. 搜索输入 XML 中的所有属性值并替换文本

  3. 复制其他节点以输出

  4. 复制处理器指令和注释以输出

  5. 匹配和处理输入中的特定节点

我面临的问题是:

一个。不确定术语(请参阅下面文件中的评论)以及我在攻击此问题时采取的策略

二.上面的模板 (5) 每当它与节点匹配时,似乎都会阻止其他模板(1 和 2)处理它

如果它有所作为,我将在Windows上运行它,使用Microsoft的处理器并使用XSLT 1.0。我已经包含了输入(Input.xml),XSLT(Transform.xslt)和输出(Output.xml)的简化版本。

我确实尝试使用"模式"从通用搜索和替换模板(1 和 2)运行目标模板 (5),但在这种情况下,模板 1 和 2 运行,但目标模板 (5) 本身不运行。

我将不胜感激任何意见和建议。

输入.xml

<?xml version="1.0" encoding="UTF-8"?>
<List>
    <Item Name="Item1" Text="abcd"/>
    <Item Name="Itembc" Text="qrst"/>
    <Item Name="Special" Text="Hello, Worldbc"/>
    <Item Name="Special" Text=""/>
</List>

Transform.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:myjs="urn:custom-javascript" exclude-result-prefixes="msxsl myjs">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <msxsl:script language="JavaScript" implements-prefix="myjs">
    <![CDATA[
        function EscapeRegExp(str)
        {
            return str.replace(/([.*+?^=!:${}()|[]/\])/g, "\$1");
        }
        function StringReplace(strWhere, strWhat, strBy, strFlags)
        {
            return strWhere.replace ( new RegExp(EscapeRegExp(strWhat), strFlags), strBy);
        }
    ]]>
    </msxsl:script>
    <!-- ********************************************************************************************************************** -->
    <!--                                                                                                                        -->
    <!-- Because of the following 4 templates, the identity transform is not needed in this XSLT                                -->
    <!--                                                                                                                        -->
    <!-- ********************************************************************************************************************** -->
    <!-- 1 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
    <!--         Modify (1 of 4) and (2 of 4) to support additional replacement           -->
    <!-- Search and Replace in attributes -->
    <xsl:template match="@*">
        <xsl:attribute name="{name()}" namespace="{namespace-uri()}">
            <xsl:variable name="TempAttrValue" select="."/>
            <xsl:value-of select="myjs:StringReplace(string($TempAttrValue), 'bc', '2', 'g')"/>
        </xsl:attribute>
        <!-- xsl:apply-templates mode="TargetedTemplate"/ -->
    </xsl:template>
    <!-- 2 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
    <!--         Modify (1 of 4) and (2 of 4) to support additional replacement           -->
    <!-- Search and Replace in data nodes -->
    <xsl:template match="text()">
        <xsl:variable name="TempTextValue" select="."/>
        <xsl:value-of select="myjs:StringReplace(string($TempTextValue), 'bc', '3', 'g')"/>
        <!-- xsl:apply-templates mode="TargetedTemplate"/ -->
    </xsl:template>
    <!-- 3 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
    <!-- Process element nodes but not attributes -->
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <!-- 4 of 4: Copy all nodes from source XML to the final XML, searching and replacing -->
    <!-- Leave the comment nodes and processing instruction nodes alone -->
    <xsl:template match="comment() | processing-instruction()">
        <xsl:copy/>
    </xsl:template>
    <!-- 5 : Process specific nodes -->
    <!-- Assumes an item in the input called Special and fills it with (No Data) if it is empty -->
    <!-- Seems to be interfering with 1-4 above. Changing the [@Name='Special'] to [@Name='SpecialA'] will let 1-4 above to work -->
    <xsl:template match="Item[@Name='Special']/@Text">
        <xsl:attribute name="Text">
            <xsl:variable name="TempSpecialText" select="."/>
            <xsl:choose>
                <xsl:when test="($TempSpecialText = '')">(No Data)</xsl:when>
                <xsl:otherwise><xsl:value-of select="$TempSpecialText"/></xsl:otherwise>
            </xsl:choose>
        </xsl:attribute>
        <xsl:apply-templates/>
    </xsl:template>
</xsl:stylesheet>

输出.xml

<?xml version="1.0" encoding="UTF-8"?>
<List>
    <Item Name="Item1" Text="a2d">
    </Item>
    <Item Name="Item2" Text="qrst">
    </Item>
    <Item Name="Special" Text="Hello, Worldbc">
    </Item>
    <Item Name="Special" Text="(No Data)">
    </Item>
</List>

输出 - 所需.xml

<?xml version="1.0" encoding="UTF-8"?>
<List>
    <Item Name="Item1" Text="a2d">
    </Item>
    <Item Name="Item2" Text="qrst">
    </Item>
    <Item Name="Special" Text="Hello, World2">
    </Item>
    <Item Name="Special" Text="(No Data)">
    </Item>
</List>

我认为主要问题在于您的最终模板

<xsl:template match="Item[@Name='Special']/@Text">
    <xsl:attribute name="Text">
        <xsl:variable name="TempSpecialText" select="."/>
        <xsl:choose>
            <xsl:when test="($TempSpecialText = '')">(No Data)</xsl:when>
            <xsl:otherwise><xsl:value-of select="$TempSpecialText"/></xsl:otherwise>
        </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates/>
</xsl:template>

这将在仅与@*匹配的模板之前匹配您的<Item Name="Special" Text="Hello, Worldbc">元素。但是,Text 属性不为空,您的xsl:otherwise只是再次输出该值,而不会执行所需的替换。此处的<xsl:apply-templates/>是不必要的,因为属性没有任何可供选择的子节点。

你能做的是这个...

<xsl:template match="Item[@Name='Special']/@Text">
    <xsl:attribute name="Text">
        <xsl:variable name="TempSpecialText" select="."/>
        <xsl:choose>
            <xsl:when test="($TempSpecialText = '')">(No Data)</xsl:when>
            <xsl:otherwise>
                  <xsl:value-of select="myjs:StringReplace(string($TempSpecialText), 'bc', '2', 'g')"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates/>
</xsl:template>

但这是代码的重复。更好的解决方案是更改模板仅匹配空属性,如下所示:

<xsl:template match="Item[@Name='Special']/@Text[. = '']">
    <xsl:attribute name="Text">(No Data)</xsl:attribute>
</xsl:template>

这样,它将仅匹配<Item Name="Special" Text=""/>元素,其中<Item Name="Special" Text="Hello, Worldbc"/>将由通用@*模板匹配。

谨慎使用Microsoft特定的扩展函数,因为这显然限制了您在Microsoft平台上运行。如果限制为 XSLT 1.0,则意味着使用递归模板。(请参阅在 xslt 中查找和替换实体作为示例)。或者,如果可以切换到 XSLT 2.0,则 replace 函数是标准配置。

这对你有用吗?

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:myjs="urn:custom-javascript" 
exclude-result-prefixes="msxsl myjs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<msxsl:script language="JavaScript" implements-prefix="myjs">
<![CDATA[
    function EscapeRegExp(str)
    {
        return str.replace(/([.*+?^=!:${}()|[]/\])/g, "\$1");
    }
    function StringReplace(strWhere, strWhat, strBy, strFlags)
    {
        return strWhere.replace ( new RegExp(EscapeRegExp(strWhat), strFlags), strBy);
    }
]]>
</msxsl:script>
<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="@*[contains(., 'bc')]">
    <xsl:attribute name="{name()}" namespace="{namespace-uri()}">
        <xsl:value-of select="myjs:StringReplace(string(.), 'bc', '2', 'g')"/>
    </xsl:attribute>
</xsl:template>
<xsl:template match="@*[not(string())]">
    <xsl:attribute name="{name()}" namespace="{namespace-uri()}">
        <xsl:text>(No Data)</xsl:text>
    </xsl:attribute>
</xsl:template>
</xsl:stylesheet>

相关内容

  • 没有找到相关文章