如何用XSLT的preceding-sibling获取前一个非空Meter值?
解决XSLT中向下填充缺失Meter/State值的问题
针对你遇到的CSV转XML后明细行缺失表头字段、无法通过preceding-sibling获取前序非空值的问题,以下是两种可行的修正方案,适配不同的XML结构场景:
场景假设
先明确典型的XML结构(你可根据实际结构调整):
<Rows> <!-- 表头行:含Meter/State,无业务数据 --> <Row> <Meter>Meter001</Meter> <State>CA</State> <StartDate></StartDate> <Volume></Volume> </Row> <!-- 明细行:仅含业务数据,缺失Meter/State --> <Row> <Meter></Meter> <State></State> <StartDate>2024-01-01</StartDate> <Volume>100</Volume> </Row> <Row> <Meter></Meter> <State></State> <StartDate>2024-01-02</StartDate> <Volume>150</Volume> </Row> </Rows>
方案1:使用key分组关联表头与明细行
适合表头行(带Meter/State)与后续明细行连续分组的场景,效率更高:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义key:将明细行关联到最近的前置表头行 --> <xsl:key name="detail-group" match="Row[StartDate!='']" use="generate-id(preceding-sibling::Row[Meter[normalize-space()] and State[normalize-space()]][1])"/> <xsl:template match="/"> <Result> <!-- 遍历所有表头行,批量处理对应明细 --> <xsl:for-each select="Rows/Row[Meter[normalize-space()] and State[normalize-space()]]"> <xsl:variable name="current-meter" select="Meter"/> <xsl:variable name="current-state" select="State"/> <!-- 输出填充后的明细行 --> <xsl:for-each select="key('detail-group', generate-id())"> <Row> <Meter><xsl:value-of select="$current-meter"/></Meter> <State><xsl:value-of select="$current-state"/></State> <StartDate><xsl:value-of select="StartDate"/></StartDate> <Volume><xsl:value-of select="Volume"/></Volume> </Row> </xsl:for-each> </xsl:for-each> </Result> </xsl:template> </xsl:stylesheet>
方案2:递归查找最近的非空字段值
适合表头行与明细行无严格连续分组的场景,灵活性更强:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 主模板:仅处理含StartDate的有效行 --> <xsl:template match="/"> <Result> <xsl:apply-templates select="Rows/Row[StartDate!='']"/> </Result> </xsl:template> <!-- 处理明细行,填充缺失字段 --> <xsl:template match="Row[StartDate!='']"> <Row> <Meter> <xsl:call-template name="find-last-non-empty"> <xsl:with-param name="node" select="preceding-sibling::Row[1]"/> <xsl:with-param name="field" select="'Meter'"/> </xsl:call-template> </Meter> <State> <xsl:call-template name="find-last-non-empty"> <xsl:with-param name="node" select="preceding-sibling::Row[1]"/> <xsl:with-param name="field" select="'State'"/> </xsl:call-template> </State> <StartDate><xsl:value-of select="StartDate"/></StartDate> <Volume><xsl:value-of select="Volume"/></Volume> </Row> </xsl:template> <!-- 递归模板:向上查找最近的非空字段值 --> <xsl:template name="find-last-non-empty"> <xsl:param name="node"/> <xsl:param name="field"/> <xsl:choose> <!-- 当前节点对应字段非空,返回值 --> <xsl:when test="$node and $node/*[local-name()=$field][normalize-space()]"> <xsl:value-of select="$node/*[local-name()=$field]"/> </xsl:when> <!-- 当前节点为空,继续向上递归查找 --> <xsl:when test="$node"> <xsl:call-template name="find-last-non-empty"> <xsl:with-param name="node" select="$node/preceding-sibling::Row[1]"/> <xsl:with-param name="field" select="$field"/> </xsl:call-template> </xsl:when> <!-- 无匹配值时返回默认标识 --> <xsl:otherwise> <xsl-text>N/A</xsl-text> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
常见问题修正
如果你之前用preceding-sibling::Row[Meter!=''][1]无效,大概率是这两个原因:
- 空值判断不准确:原XML中的空值可能是空白字符而非空字符串,需用
Meter[normalize-space()]代替Meter!='',normalize-space()会自动过滤空白。 - 上下文节点错误:如果先筛选了有效行,
preceding-sibling是基于原XML节点位置而非筛选后的结果集,此时需将筛选后的行存入变量,再通过位置索引查找前序节点。
内容的提问来源于stack exchange,提问作者NealWalters
相关产品推荐
相关产品推荐

