【问题标题】:XSLT to text - Child nodes with same name- Check for last child of each parentXSLT 到文本 - 具有相同名称的子节点 - 检查每个父节点的最后一个子节点
【发布时间】:2018-05-30 04:45:50
【问题描述】:

我需要使用 XSLT 将 XML 转换为文本。我得到的输出包含一个块中的所有数据,没有分隔符或换行符,我觉得 XSLT 中的位置部分甚至没有被执行。

问题:我想测试每个父母的最后一个孩子的位置,如果它是父母(currentRow)的最后一个孩子(列值),则包含一个新行,如果不是,则包含一个分隔符.

我有一个如下所示的 XML:

XML 文件:

<?xml version='1.0'?>
<webRowSet xmlns='http://java.sun.com/xml/ns/jdbc'>
<data>
<currentRow>
<columnValue><![CDATA[]]></columnValue>
<columnValue><![CDATA[26068384]]></columnValue>
<columnValue><![CDATA[070-0010055-4842MAR18]]></columnValue>
<columnValue>2018-04-25</columnValue>
<columnValue>170.310</columnValue>
<columnValue><![CDATA[UI-004058]]></columnValue>
</currentRow>
<currentRow><columnValue><![CDATA[]]></columnValue>
<columnValue><![CDATA[26068385]]></columnValue>
<columnValue><![CDATA[070-0010058-5739MAR18]]></columnValue>
<columnValue>2018-04-25</columnValue>
<columnValue>209.900</columnValue>
<columnValue><![CDATA[UI-004057]]></columnValue>
</currentRow>
</data></webRowSet>

XSLT:

<?xml version="1.0"?>
<xsl:stylesheet version ="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="Delimiter"><xsl:text>|</xsl:text></xsl:variable>
<xsl:variable name="NewLine"><xsl:text>&#10;</xsl:text></xsl:variable>
<xsl:template match="/">
<xsl:text>EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo</xsl:text>
<xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="webRowSet/data">
<xsl:for-each select="currentRow">
<xsl:for-each select="columnValue">
<xsl:value-of select="."/>
<xsl:variable name="Rec_position" select="count(../preceding-sibling::columnValue)+1"/>
<xsl:if test="$Rec_position=6"><xsl:value-of select="$NewLine"/></xsl:if>
<xsl:if test="$Rec_position!=6"><xsl:value-of select="$Delimiter"/></xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

TEXT 中的输出应该是这样的:

EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo
   |26068384|070-0010055-4842MAR18|2018-04-25|170.310|UI-004058
   |26068385|070-0010058-5739MAR18|2018-04-25|209.900|UI-004057

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    您的 XSLT 有两个问题。

    首先,您没有考虑到 XML 中的元素位于默认命名空间中的事实,正如您的根元素上的 xmlns='http://java.sun.com/xml/ns/jdbc 所定义的那样。

    如果您确实只使用 XSLT 1.0,则必须将该名称空间与 XSLT 中的前缀相关联,如下所示...

    xmlns:j="http://java.sun.com/xml/ns/jdbc"
    

    然后在选择表达式中的所有元素之前使用该前缀。比如……

     <xsl:template match="j:webRowSet/j:data">
    

    其次,在你的count 表达式中,.. 代表父节点,当你真的想计算当前节点的前面的兄弟姐妹时,你应该这样写......

     <xsl:variable name="Rec_position" select="count(preceding-sibling::j:columnValue)+1"/>
    

    虽然position() 函数在这里会做同样的事情。

    <xsl:variable name="Rec_position" select="position()"/>
    

    但是您真的想将列数硬编码为 6 吗?试试这个 XSLT:

    <xsl:stylesheet version ="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:j="http://java.sun.com/xml/ns/jdbc">
    <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
    
    <xsl:strip-space elements="*"/>
    
    <xsl:variable name="Delimiter"><xsl:text>|</xsl:text></xsl:variable>
    <xsl:variable name="NewLine"><xsl:text>&#10;</xsl:text></xsl:variable>
    
    <xsl:template match="/">
      <xsl:text>EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo</xsl:text>
      <xsl:value-of select="$NewLine"/>
      <xsl:apply-templates/>
    </xsl:template>
    
    <xsl:template match="j:webRowSet/j:data">
      <xsl:for-each select="j:currentRow">
        <xsl:for-each select="j:columnValue">
          <xsl:if test="position() != 1"><xsl:value-of select="$Delimiter" /></xsl:if>
          <xsl:value-of select="."/>
        </xsl:for-each>
        <xsl:value-of select="$NewLine"/>
      </xsl:for-each>
    </xsl:template>
    </xsl:stylesheet>
    

    注意,如果你可以使用 XSLT 2.0,你可以这样写:

    <xsl:stylesheet version ="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                    xpath-default-namespace="http://java.sun.com/xml/ns/jdbc">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:variable name="NewLine"><xsl:text>&#10;</xsl:text></xsl:variable>
    
    <xsl:template match="/">
      <xsl:text>EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo</xsl:text>
      <xsl:value-of select="$NewLine"/>
      <xsl:apply-templates/>
    </xsl:template>
    
    <xsl:template match="webRowSet/data">
      <xsl:for-each select="currentRow">
        <xsl:value-of select="columnValue" separator="|"/>
        <xsl:value-of select="$NewLine"/>
      </xsl:for-each>
    </xsl:template>
    </xsl:stylesheet>
    

    注意xpath-default-namespace 的使用使得处理默认命名空间更容易。此外,在 xsl:value-of 上使用 separator,这适用于选择多个值时。 (在 XSLT 1.0 中,xsl:value-of 只会显示第一个值)。

    【讨论】:

    • 非常感谢!像魅力一样工作。我是 XSLT 的新手,这是我的第一个练习。感谢对此的及时回应!
    【解决方案2】:

    您忘记考虑命名空间。所以将xmlns:j="http://java.sun.com/xml/ns/jdbc" 添加到XSLT 文件的xsl:stylesheet 元素中。这样您就可以使用正确的命名空间访问所有元素。

    关于命名空间,将您的最后一个模板更改为:

    <xsl:template match="j:webRowSet/j:data">
        <xsl:for-each select="j:currentRow">
          <xsl:for-each select="j:columnValue">
            <xsl:value-of select="."/>
            <xsl:variable name="Rec_position" select="position()"/>
            <xsl:if test="$Rec_position=6"><xsl:value-of select="$NewLine"/></xsl:if>
            <xsl:if test="$Rec_position &lt; 6"><xsl:value-of select="$Delimiter"/></xsl:if>
          </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
    

    输出为:

    EXPR5_5|VOUCHER_ID|INVOICE_ID|ENTERED_DT|GROSS_AMT|UnifierRecordNo
    |26068384|070-0010055-4842MAR18|2018-04-25|170.310|UI-004058
    |26068385|070-0010058-5739MAR18|2018-04-25|209.900|UI-004057
    

    这是一个非常简单的解决方案,只需进行最少的修改。

    【讨论】:

    • 谢谢!希望我能标记 2 个正确答案:) 让我开心。
    猜你喜欢
    • 2017-05-04
    • 2017-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    相关资源
    最近更新 更多