如何在 XSLT 中对子节点求和
How to Sum sub nodes in XSLT
我有传入的 xml XSLT 结构如下代码。
<?xml version="1.0" encoding="UTF-8"?>
<TimeAccount>
<TimeAccount>
<userId>123</userId>
<timeAccountDetails>
<TimeAccountDetail>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-01-01T00:00:00.000</bookingDate>
<bookingAmount>190</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-19T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-20T00:00:00.000</bookingDate>
<bookingAmount>120</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-21T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
</timeAccountDetails>
</TimeAccount>
<TimeAccount>
<userId>456</userId>
<timeAccountDetails>
<TimeAccountDetail>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-01-01T00:00:00.000</bookingDate>
<bookingAmount>190</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-19T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-06-01T00:00:00.000</bookingDate>
<bookingAmount>100</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-21T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
</timeAccountDetails>
</TimeAccount>
</TimeAccount>
我希望通过为每个 userId 添加 bookingAmount 得到如下输出
<TimeAccount>
<TimeAccount>
<ID>123</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>310</bookingAmount>
<TimeAccount>
<TimeAccount>
<ID>456</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>290</bookingAmount>
<TimeAccount>
</TimeAccount>
我如下编写 xslt,使用 for-each 和 if 条件,然后使用 SUM 函数。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<TimeAccount>
<xsl:apply-templates/>
</TimeAccount>
</xsl:template>
<xsl:template match="TimeAccount/TimeAccount">
<TimeAccount>
<ID><xsl:value-of select="userId"/></ID>
<bookingAmount>
<xsl:for-each select="timeAccountDetails/TimeAccountDetail">
<xsl:if test="bookingType != 'EMPLOYEE_TIME'">
<xsl:value-of select="sum(bookingAmount)"/>
</xsl:if>
</xsl:for-each>
</TimeAccount>
</xsl:template>
</xsl:stylesheet>
但输出并没有添加 bookingAmounts 节点,而是并排打印节点。
<TimeAccount>
<TimeAccount>
<ID>123</ID>
<bookingAmount>190 120</bookingAmount>
<TimeAccount>
<TimeAccount>
<ID>456</ID>
<bookingAmount>190 100</bookingAmount>
<TimeAccount>
<TimeAccount>
我做错了什么?我尝试在预订金额之前保留 /,例如打印 null 的 sum(/bookingAmount),如果我输入 // 那么它会打印所有用户的预订金额总数。
将您的 XSLT 代码更改为以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/TimeAccount">
<xsl:copy>
<xsl:apply-templates select="TimeAccount" />
</xsl:copy>
</xsl:template>
<xsl:template match="TimeAccount">
<xsl:copy>
<ID><xsl:value-of select="userId"/></ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount><xsl:value-of select="sum(timeAccountDetails/TimeAccountDetail[bookingType != 'EMPLOYEE_TIME']/bookingAmount)"/></bookingAmount>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出应该符合预期:
<?xml version="1.0"?>
<TimeAccount>
<TimeAccount>
<ID>123</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>310</bookingAmount>
</TimeAccount>
<TimeAccount>
<ID>456</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>290</bookingAmount>
</TimeAccount>
</TimeAccount>
我将 <bookingType>
固定为“MANUAL_ADJUSTMENT”,因为您只需要这些值(所有不是 EMPLOYEE_TIME
的值)。
顺便说一句,您只需要 XSLT-1.0 即可完成此任务。 XSLT-3.0 功能只是一个额外的好处。
这个 xslt 给出了预期的结果:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="userId">
<ID>
<xsl:value-of select="text()"/>
</ID>
</xsl:template>
<xsl:template match="timeAccountDetails">
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>
<xsl:value-of select="sum(TimeAccountDetail[bookingType='MANUAL_ADJUSTMENT']/bookingAmount)"/>
</bookingAmount>
</xsl:template>
</xsl:stylesheet>
当您使用 xslt 3.0 时,您可以使用
<xsl:mode on-no-match="shallow-copy"/>
与以下相同:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
并且只会复制您没有模板匹配的任何内容。
我有传入的 xml XSLT 结构如下代码。
<?xml version="1.0" encoding="UTF-8"?>
<TimeAccount>
<TimeAccount>
<userId>123</userId>
<timeAccountDetails>
<TimeAccountDetail>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-01-01T00:00:00.000</bookingDate>
<bookingAmount>190</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-19T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-20T00:00:00.000</bookingDate>
<bookingAmount>120</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-21T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
</timeAccountDetails>
</TimeAccount>
<TimeAccount>
<userId>456</userId>
<timeAccountDetails>
<TimeAccountDetail>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-01-01T00:00:00.000</bookingDate>
<bookingAmount>190</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-19T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-06-01T00:00:00.000</bookingDate>
<bookingAmount>100</bookingAmount>
</TimeAccountDetail>
<TimeAccountDetail>
<employeeTime>e885f88ccaa647abb00a8f84bcf5aa32</employeeTime>
<bookingType>EMPLOYEE_TIME</bookingType>
<bookingUnit>HOURS</bookingUnit>
<bookingDate>2021-04-21T00:00:00.000</bookingDate>
<bookingAmount>-8</bookingAmount>
</TimeAccountDetail>
</timeAccountDetails>
</TimeAccount>
</TimeAccount>
我希望通过为每个 userId 添加 bookingAmount 得到如下输出
<TimeAccount>
<TimeAccount>
<ID>123</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>310</bookingAmount>
<TimeAccount>
<TimeAccount>
<ID>456</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>290</bookingAmount>
<TimeAccount>
</TimeAccount>
我如下编写 xslt,使用 for-each 和 if 条件,然后使用 SUM 函数。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<TimeAccount>
<xsl:apply-templates/>
</TimeAccount>
</xsl:template>
<xsl:template match="TimeAccount/TimeAccount">
<TimeAccount>
<ID><xsl:value-of select="userId"/></ID>
<bookingAmount>
<xsl:for-each select="timeAccountDetails/TimeAccountDetail">
<xsl:if test="bookingType != 'EMPLOYEE_TIME'">
<xsl:value-of select="sum(bookingAmount)"/>
</xsl:if>
</xsl:for-each>
</TimeAccount>
</xsl:template>
</xsl:stylesheet>
但输出并没有添加 bookingAmounts 节点,而是并排打印节点。
<TimeAccount>
<TimeAccount>
<ID>123</ID>
<bookingAmount>190 120</bookingAmount>
<TimeAccount>
<TimeAccount>
<ID>456</ID>
<bookingAmount>190 100</bookingAmount>
<TimeAccount>
<TimeAccount>
我做错了什么?我尝试在预订金额之前保留 /,例如打印 null 的 sum(/bookingAmount),如果我输入 // 那么它会打印所有用户的预订金额总数。
将您的 XSLT 代码更改为以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/TimeAccount">
<xsl:copy>
<xsl:apply-templates select="TimeAccount" />
</xsl:copy>
</xsl:template>
<xsl:template match="TimeAccount">
<xsl:copy>
<ID><xsl:value-of select="userId"/></ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount><xsl:value-of select="sum(timeAccountDetails/TimeAccountDetail[bookingType != 'EMPLOYEE_TIME']/bookingAmount)"/></bookingAmount>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出应该符合预期:
<?xml version="1.0"?>
<TimeAccount>
<TimeAccount>
<ID>123</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>310</bookingAmount>
</TimeAccount>
<TimeAccount>
<ID>456</ID>
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>290</bookingAmount>
</TimeAccount>
</TimeAccount>
我将 <bookingType>
固定为“MANUAL_ADJUSTMENT”,因为您只需要这些值(所有不是 EMPLOYEE_TIME
的值)。
顺便说一句,您只需要 XSLT-1.0 即可完成此任务。 XSLT-3.0 功能只是一个额外的好处。
这个 xslt 给出了预期的结果:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="userId">
<ID>
<xsl:value-of select="text()"/>
</ID>
</xsl:template>
<xsl:template match="timeAccountDetails">
<bookingType>MANUAL_ADJUSTMENT</bookingType>
<bookingAmount>
<xsl:value-of select="sum(TimeAccountDetail[bookingType='MANUAL_ADJUSTMENT']/bookingAmount)"/>
</bookingAmount>
</xsl:template>
</xsl:stylesheet>
当您使用 xslt 3.0 时,您可以使用
<xsl:mode on-no-match="shallow-copy"/>
与以下相同:
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
并且只会复制您没有模板匹配的任何内容。