xml - 如何使用 XSLT 1 计算 XML 元素中的值
问题描述
我有一组 XML 文档,其中包含单个 XML 元素中的一些大型值列表。我需要确定每个列表有多大,并且只在它们太大时输出计数。我需要使用仅支持 1.0 的 xsltproc,并尝试使用 count() 函数,但它似乎不会产生除 1 以外的任何值。示例样式表是:
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
<!-- NOTE: US-ASCII encoding is not compatible with Java HTML text -->
<xsl:output method="html" indent="yes" encoding="ASCII"/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title><xsl:value-of select="'Test Case for count()'"/></title>
</head>
<body>
<xsl:element name="table">
<xsl:attribute name="border">1</xsl:attribute>
<xsl:attribute name="align">center</xsl:attribute>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel" select="'count'"/>
<xsl:with-param name="DataValue" select="function-available('count')"/>
</xsl:call-template>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel" select="'normalize-space'"/>
<xsl:with-param name="DataValue" select="function-available('normalize-space')"/>
</xsl:call-template>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel" select="'string-length'"/>
<xsl:with-param name="DataValue" select="function-available('string-length')"/>
</xsl:call-template>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel" select="'replace'"/>
<xsl:with-param name="DataValue" select="function-available('replace')"/>
</xsl:call-template>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel" select="'tokenize'"/>
<xsl:with-param name="DataValue" select="function-available('tokenize')"/>
</xsl:call-template>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel" select="'contains'"/>
<xsl:with-param name="DataValue" select="function-available('contains')"/>
</xsl:call-template>
<xsl:variable name="DataIn" select="' A B C '"/>
<xsl:variable name="DataList">
<xsl:call-template name="Tokenize-Str">
<xsl:with-param name="Data" select="$DataIn"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel"
select="concat('tokenize(',$DataIn,')')"/>
<xsl:with-param name="DataValue">
<xsl:call-template name="Tokenize-Str">
<xsl:with-param name="Data" select="$DataIn"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="DblColTableDataRow">
<xsl:with-param name="DataLabel"
select="concat('count(',$DataIn,')')"/>
<xsl:with-param name="DataValue">
<xsl:copy-of select="$DataList"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="count(($DataList))"/>
</xsl:with-param>
</xsl:call-template>
</xsl:element>
</body>
</html>
</xsl:template>
<xsl:template name="DblColTableDataRow">
<xsl:param name="DataLabel" select="'?:'"/>
<xsl:param name="DataValue" select="'???'"/>
<xsl:element name="tr">
<xsl:element name="td">
<xsl:attribute name="style">text-align:right</xsl:attribute>
<xsl:copy-of select="$DataLabel"/>
</xsl:element>
<xsl:element name="td">
<xsl:copy-of select="$DataValue"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- template needed because tokenize function not supported -->
<xsl:template name="Tokenize-Str">
<xsl:param name="Data"/>
<xsl:variable name="DataStr">
<xsl:value-of select="normalize-space($Data)"/>
</xsl:variable>
<xsl:if test="0 != string-length($DataStr)">
<!--xsl:value-of select="concat('Tkn-Str(',$Data,')')"/-->
<xsl:choose>
<xsl:when test="contains($DataStr,' ')">
<xsl:element name="tkn">
<xsl:value-of select="substring-before($DataStr, ' ')"/>
</xsl:element>
<xsl:call-template name="Tokenize-Str">
<xsl:with-param name="Data"
select="substring-after($DataStr, ' ')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="tkn">
<xsl:value-of select="$DataStr"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
这是为了使 XML 文档内容无关紧要。命令:
xsltproc -o tst.html test_case.xsl whatever.xml
产生:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=ASCII">
<title>Test Case for count()</title></head><body><table border="1" align="center"><tr xmlns="">
<td style="text-align:right">count</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">normalize-space</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">string-length</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">replace</td>
<td>false</td>
</tr>
<tr xmlns="">
<td style="text-align:right">tokenize</td>
<td>false</td>
</tr>
<tr xmlns="">
<td style="text-align:right">contains</td>
<td>true</td>
</tr>
<tr xmlns="">
<td style="text-align:right">tokenize( A B C )</td>
<td>
<tkn>A</tkn><tkn>B</tkn><tkn>C</tkn>
</td>
</tr>
<tr xmlns="">
<td style="text-align:right">count( A B C )</td>
<td>
<tkn>A</tkn><tkn>B</tkn><tkn>C</tkn>: 1</td>
</tr></table></body></html>
我不确定为什么我的计数为 1,因为我的模板显然返回了 3 个元素节点。
解决方案
xsl:element
使用纯 XSLT 1,包含使用或文字结果元素创建的结果节点的任何变量都是结果树片段https://www.w3.org/TR/xslt-10/#section-Result-Tree-Fragments这是一种数据结构与您从输入文档中获得的节点集非常不同。
所以你的变量$DataList
是这样一个结果树片段,你可以输出,xsl:copy-of
但你不能在它的内容上使用 XPath,因为你需要一个扩展函数,比如exsl:node-set
(http://exslt.org/exsl/index.html)例如<xsl:value-of select="count(exsl:node-set($DataList)/*)" xmlns:exsl="http://exslt.org/common"/>
会给您要查找的计数(因为该exsl:node-set
函数将您的结果树片段转换为包含您的结果元素节点的根节点)。
请注意,xsltproc 应该支持http://exslt.org/str/functions/tokenize/index.html,因此您应该能够简单地使用,例如,在您的样式表中使用<xsl:value-of select="count(str:tokenize('A B C', ' '))"/>
适当的命名空间声明。xmlns:str="http://exslt.org/strings"
推荐阅读
- reactjs - 使用 JSON Schema Form (Retool) 输入多个文件
- azure-web-app-service - 在 Azure WebApps 中使用 Tomcat 的 SAML
- c# - 如何递归地填充数组
- numpy - 服务器电脑上的 Numpy 慢
- powershell - 使用 ForEach 而不是 pipe 和 % 编写循环
- excel - 协助使用 Excel VBA 从在线 Web 服务解析 XML
- unity3d - 如何在 Blender 2.9 中重新连接电枢
- ubuntu - 无法在 jetson nano 上安装 OpenCV-4.5.0
- java - 如何在spring legacy环境下正常运行junit test?
- google-drive-api - 在 Google Drive API v3 中获取上一页令牌