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

时间:2016-02-25 23:25:16

标签: templates xslt-1.0

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

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

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

  3. 复制其他节点以输出

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

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

  6. 我面临的问题是:

    一个。不确定术语(参见下面文件中的评论)以及我攻击这个术语的方法

    B中。上面的模板(5),只要它与节点匹配,似乎阻止其他模板(1和2)处理它

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

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

    我感谢任何意见和建议。

    Input.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>
    

    的Output.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>
    

    输出 - Desired.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>
    

2 个答案:

答案 0 :(得分:1)

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

<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,则表示使用递归模板。 (以Find and replace entity in xslt为例)。或者,如果您可以切换到XSLT 2.0,则replace功能是标准功能。

答案 1 :(得分:0)

这对你有用吗?

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