我是XSL的新手,仍然不确定我的一些术语。我目前正面临着一些我似乎无法破解的事情。我正在尝试
-
搜索输入 XML 的所有数据节点(叶元素?)并替换文本
-
搜索输入 XML 中的所有属性值并替换文本
-
复制其他节点以输出
-
复制处理器指令和注释以输出
-
匹配和处理输入中的特定节点
我面临的问题是:
一个。不确定术语(请参阅下面文件中的评论)以及我在攻击此问题时采取的策略
二.上面的模板 (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>