使用 XSLT 的多行 [英] Multiple rows using XSLT

查看:18
本文介绍了使用 XSLT 的多行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果有人可以帮助我,我很感激.我有一个如下所示的 XML:

<工人><Primary_Key>12345</Primary_Key><First_Name>ABC</First_Name><Last_Name>DEF</Last_Name><Multiple_Data><代码>MO</代码><数量>35</数量></Multiple_Data><Multiple_Data><代码>PA</代码><数量>45</数量></Multiple_Data></工人><工人><Primary_Key>67890</Primary_Key><First_Name>GHI</First_Name><Last_Name>JKL</Last_Name><Multiple_Data><代码>PA</代码><数量>25</数量></Multiple_Data></工人><工人><Primary_Key>11121</Primary_Key><First_Name>MNO</First_Name><Last_Name>PQR</Last_Name></worker</root>

而且我需要有如下所示的输出:

<块引用>

12345,ABC,DEF,MO,35
12345,ABC,DEF,PA,45
67890,GHI,JKL,PA,25
11121,MNO,PQR,,,

12345 有多个数据代码和数量.值显示在单独的行上.11121 没有代码和数量,所以分隔符 ,, 是唯一显示的.

这是 XSLT,但似乎不起作用:

<变量名称="分隔符"><text>,</text></variable><variable name="linefeed" select="'&#xd;&#xa;'"/><模板匹配="child::Primary_Key"><选择><when test="preceding-sibling::Multiple_Data//child::text() = ''"><for-each select="following-sibling::Multiple_Data"><value-of select="concat(preceding-sibling::Primary_Key/child::text()、$delimiter、preceding-sibling::First_Name/child::text()、$delimiter、preceding-sibling::Last_Name/child::text(), $delimiter, child::Code/child::text(), $delimiter, child::Amount/child::text())"/><if test="(self::Primary_Key[position() = last()])"><value-of select="$linefeed"/></如果></for-each></何时><否则><for-each select="child::Primary_Key"><value-of select="concat(preceding-sibling::Primary_Key/child::text()、$delimiter、preceding-sibling::First_Name/child::text()、$delimiter、preceding-sibling::Last_Name/child::text(), $delimiter, $delimiter )"/><if test="(self::Primary_Key[position() = last()])"><value-of select="$linefeed"/></如果></for-each></否则></选择><模板匹配=child::root/worker"><apply-templates select="child::Primary_Key"/></转换>

当我尝试在 XSLT 下使用它时,它在顶部和空格处给了我不必要的新行.if(position() > 1) 无法摆脱输出中的第一个空行.当然,这里没有显示没有多个数据的worker.

<输出方法=文本"/><变量名称="分隔符"><text>,</text></variable><variable name="linefeed" select="'&#xd;&#xa;'"/><模板匹配="child::Primary_Key"><for-each select="following-sibling::Multiple_Data"><value-of select="concat(preceding-sibling::Primary_Key/child::text()、$delimiter、preceding-sibling::First_Name/child::text()、$delimiter、preceding-sibling::Last_Name/child::text(), $delimiter, child::Code/child::text(), $delimiter, child::Amount/child::text())"/><if test="(self::Primary_Key[position() = last()])"><value-of select="$linefeed"/></如果></for-each><模板匹配=child::root/worker"><apply-templates select="child::Primary_Key"/>

是否有其他更好的方法来编码而不在标题中使用此转换?

<块引用>

我想创建以以下内容开头的 xslt:

但看起来像孩子,前兄弟和后兄弟::代码不受支持.

非常感谢您的帮助.

解决方案

鉴于您可以使用 XSLT 2 并希望为输入样本中不存在的数据提供一些输出,我建议使用两步转换,其中单独模式的第一步只是为缺失的数据添加空元素,然后默认模式的第二步输出 CSV.

如果您可以在 oXygen 和 XSLT 3 中使用 Saxon 9.8(任何版本)或 9.7 PE/EE,您可以轻松设置其他模式

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"版本=3.0"><xsl:output method="text"/><xsl:mode name="add-dummy-data" on-no-match="shallow-copy"/><xsl:variable name="dummy-data"><xsl:apply-templates mode="add-dummy-data"/></xsl:变量><xsl:template match="worker[not(Multiple_Data)]" mode="add-dummy-data"><xsl:copy><xsl:copy-of select="@*, node()"/><Multiple_Data><代码/><金额/></Multiple_Data></xsl:copy></xsl:模板><xsl:template match="/"><xsl:apply-templates select="$dummy-data/root/worker/Multiple_Data"/></xsl:模板><xsl:template match="Multiple_Data"><xsl:value-of select="../Primary_Key, ../First_Name, ../Last_Name, Code, Amount" separator=","/><xsl:text>&#10;</xsl:text></xsl:模板></xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bdxtq1/1

如果您绑定到 XSLT 2,则需要使用

<xsl:copy><xsl:apply-templates select="@* | node()" mode="#current"/></xsl:copy></xsl:模板>

代替<xsl:mode name="add-dummy-data" on-no-match="shallow-copy"/>

I appreciate if anyone could help me on this. I have an XML that looks like this:

<root>
<worker>
     <Primary_Key>12345</Primary_Key>
     <First_Name>ABC</First_Name>
     <Last_Name>DEF</Last_Name>         
    <Multiple_Data>
        <Code>MO</Code>
        <Amount>35</Amount>
    </Multiple_Data>
    <Multiple_Data>
        <Code>PA</Code>
        <Amount>45</Amount>
    </Multiple_Data>
</worker>
<worker>
    <Primary_Key>67890</Primary_Key>
    <First_Name>GHI</First_Name>
    <Last_Name>JKL</Last_Name>
    <Multiple_Data>
        <Code>PA</Code>
        <Amount>25</Amount>
    </Multiple_Data>
</worker>
<worker>
    <Primary_Key>11121</Primary_Key>
    <First_Name>MNO</First_Name>
    <Last_Name>PQR</Last_Name>    
</worker</root>

And I need to have the output that will exactly looks like this:

12345,ABC,DEF,MO,35
12345,ABC,DEF,PA,45
67890,GHI,JKL,PA,25
11121,MNO,PQR,,

12345 has multiple data Codes and Amounts. Values are displayed on separate rows. 11121 doesn't have code and amount so the delimiter ,, is the only one displayed.

Here's the XSLT but seems not working:

<output method="text"/>

<variable name="delimiter"><text>,</text></variable>    
<variable name="linefeed" select="'&#xd;&#xa;'"/>
<template match="child::Primary_Key">     
    <choose>
        <when test="preceding-sibling::Multiple_Data//child::text() = ''">
            <for-each select="following-sibling::Multiple_Data">

                <value-of select="concat(preceding-sibling::Primary_Key/child::text(), $delimiter, preceding-sibling::First_Name/child::text(), $delimiter, preceding-sibling::Last_Name/child::text(), $delimiter, child::Code/child::text(), $delimiter, child::Amount/child::text())"/>

                <if test="(self::Primary_Key[position() = last()])">
                    <value-of select="$linefeed"/> 
                </if>

            </for-each>
        </when>
        <otherwise>
            <for-each select="child::Primary_Key">                    
                <value-of select="concat(preceding-sibling::Primary_Key/child::text(), $delimiter, preceding-sibling::First_Name/child::text(), $delimiter, preceding-sibling::Last_Name/child::text(), $delimiter, $delimiter )"/>                
                <if test="(self::Primary_Key[position() = last()])">
                    <value-of select="$linefeed"/> 
                </if>

            </for-each>

        </otherwise>

    </choose>                        
</template>
<template match="child::root/worker">
    <apply-templates select="child::Primary_Key"/>
</template>
</transform>

When I try to use this below XSLT, it's giving me unnecessary new line at the top and spaces. if(position() > 1) is not working to get rid the first blank line in my output. And of course, worker who doesn't have multiple data is not displayed here.

<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">

<output method="text"/>

<variable name="delimiter"><text>,</text></variable>    
<variable name="linefeed" select="'&#xd;&#xa;'"/>
<template match="child::Primary_Key">     

       <for-each select="following-sibling::Multiple_Data">

                <value-of select="concat(preceding-sibling::Primary_Key/child::text(), $delimiter, preceding-sibling::First_Name/child::text(), $delimiter, preceding-sibling::Last_Name/child::text(), $delimiter, child::Code/child::text(), $delimiter, child::Amount/child::text())"/>                
                <if test="(self::Primary_Key[position() = last()])">
                    <value-of select="$linefeed"/> 
                </if>

            </for-each>

</template>
<template match="child::root/worker">
    <apply-templates select="child::Primary_Key"/>
</template>

Are there any other better ways to code this without using this transform in the heading?

<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">

I'd like to create the xslt starting with:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

but looks like the child, preceding-sibling and following-sibling:: codes are not supported by that.

Thanks a lot for your help.

解决方案

Given that you can use XSLT 2 and want to have some output for data that does not exist in the input sample I would suggest to use a two step transformation where the first step in a separate mode simply adds empty elements for the missing data and then the second step in the default mode outputs the CSV.

If you can use Saxon 9.8 (any edition) or 9.7 PE/EE in oXygen and XSLT 3 you can easily set up the other mode with

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">

  <xsl:output method="text"/>

  <xsl:mode name="add-dummy-data" on-no-match="shallow-copy"/>

  <xsl:variable name="dummy-data">
      <xsl:apply-templates mode="add-dummy-data"/>
  </xsl:variable>

  <xsl:template match="worker[not(Multiple_Data)]" mode="add-dummy-data">
      <xsl:copy>
          <xsl:copy-of select="@*, node()"/>
          <Multiple_Data>
              <Code/>
              <Amount/>
          </Multiple_Data>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="/">
    <xsl:apply-templates select="$dummy-data/root/worker/Multiple_Data"/>
  </xsl:template>

  <xsl:template match="Multiple_Data">
      <xsl:value-of select="../Primary_Key, ../First_Name, ../Last_Name, Code, Amount" separator=","/>
      <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

see https://xsltfiddle.liberty-development.net/bdxtq1/1

if you are tied to XSLT 2 you need to use

<xsl:template match="@* | node()" mode="add-dummy-data">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

instead of <xsl:mode name="add-dummy-data" on-no-match="shallow-copy"/>

这篇关于使用 XSLT 的多行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆