我已经解决了这个问题,但我想实现一个更好的代码。我正在尝试确定发生非零金融事件的最后日期。请注意,为了简洁起见,这是一个剥离和更改的 XML,因此请原谅任何格式上的奇怪之处。我使用的实际 XML 的变化量从 2018 年 1 月 1 日到 2029 年 12 月 31 日,从 2018 年 4 月 1 日到 2029 年 12 月 31 日的所有内容总计为 0。
<Products>
<Cost>
<Company name="A"/>
<Financials>
<Change amount="-10000">
<Date date="1/1/2018" dateindays="42734">
<Type name="open">
<Change>
<Change amount="4500">
<Date date="2/1/2018" dateindays="42765">
<Type name="debit">
</Change>
<Change amount="4500">
<Date date="3/1/2018" dateindays="42793">
<Type name="debit">
</Change>
<Change amount="4500">
<Date date="4/1/2018" dateindays="42824">
<Type name="debit">
</Change>
<Change amount="-2000">
<Date date="4/1/2018" dateindays="42824">
<Type name="debit">
<Change>
<Change amount="-2500">
<Date date="4/1/2018" dateindays="42824">
<Type name="debit">
</Financials>
</Cost>
</Products>
在上面的代码片段中,我想要 3 年 1 月 2018 日的日期,因为 4 年 1 月 2018 日的日期总计为 0 借方。
使用 XSLT 2.0,我使用以下代码排除了 2018 年 4 月 1 日的日期,但我还没有找到仅返回 2018 年 3 月 1 日日期的方法。我尝试过的每件事都返回了 4/1/2018 作为该系列的最后日期。
<xsl:for-each-group select="Products/Cost[Company/@name =
$Company]/Financials/Change[Type/@name = 'Debit']/Date" group-
by="@dateindays">
<xsl:sort select="@dateindays" data-type="number" order="descending"/>
<xsl:variable name="actDate" select="@dateindays"/>
<xsl:if test="sum(/Products/Cost[Company/@name =
$Company]/Financials/Change[Type/@name = 'Debit' and Date/@dateindays =
$actDate]/@amount) != 0">
<xsl:value-of select="@date"/>
</xsl:if>
</xsl:for-each-group>
正如我所说,我做了一些黑客攻击(Excel中的隐藏单元格(,但我真的想在代码中处理它,因为看起来这个日期可能需要作为以后代码过滤的一部分。
我认为您可以使用当前组的总和进行检查,然后您需要在变量中输出这些元素,然后在该变量值中获取第一项:
<xsl:variable name="non-zero" as="element(Date)*">
<xsl:for-each-group select="Products/Cost[Company/@name = 'A']/Financials/Change[Type/@name = 'debit']" group-by="Date/@dateindays">
<xsl:sort select="current-grouping-key()" data-type="number" order="descending"/>
<xsl:variable name="actDate" select="@dateindays"/>
<xsl:if test="sum(current-group()/@amount) != 0">
<Date>{Date/@date}</Date>
</xsl:if>
</xsl:for-each-group>
</xsl:variable>
<xsl:value-of select="$non-zero[1]"/>
https://xsltfiddle.liberty-development.net/pPgCcoC
<Date>{Date/@date}</Date>
是 XSLT 3,在 XSLT 2 中您需要 <Date><xsl:value-of select="Date/@date"/></Date>
。
这是另一个类似于马丁的选项。我喜欢马丁的更好(+1(,但由于我已经写好了,所以我会继续添加它。
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="company" select="'A'"/>
<xsl:key name="changes" match="Change" use="Date/@dateindays"/>
<xsl:template match="/">
<xsl:apply-templates select="Products/Cost[Company/@name = $company]/Financials"/>
</xsl:template>
<xsl:template match="Financials">
<xsl:variable name="non-zero-days" as="xs:float*">
<xsl:for-each-group select="Change" group-by="Date/@dateindays">
<xsl:if test="sum(current-group()/@amount) != 0">
<xsl:sequence select="data(Date/@dateindays)"/>
</xsl:if>
</xsl:for-each-group>
</xsl:variable>
<results>
<xsl:value-of select="key('changes',string(max($non-zero-days)))[1]/Date/@date"/>
</results>
</xsl:template>
</xsl:stylesheet>
输出
<results>3/1/2018</results>
工作小提琴:https://xsltfiddle.liberty-development.net/3Nqn5Yn/1
我会采用两阶段的方法:首先对数据进行分组,然后对组执行所需的操作。
在第一阶段,您进行转换
<Change amount="4500">
<Date date="4/1/2018" dateindays="42824">
<Type name="debit">
</Change>
<Change amount="-2000">
<Date date="4/1/2018" dateindays="42824">
<Type name="debit">
</Change>
<Change amount="-2500">
<Date date="4/1/2018" dateindays="42824">
<Type name="debit">
</Change>
自
<Date date="4/1/2018" dateindays="42824">
<Change amount="4500">
<Type name="debit">
</Change>
<Change amount="-2000">
<Type name="debit">
</Change>
<Change amount="-2500">
<Type name="debit">
</Change>
</Date>
(这是 xsl:for-each-group 的一个简单用法(
在第二阶段,应用筛选,例如Date[sum(Change/@amount)!=0)][last()]