如何使用XSLT2.0将csv文件转换为结构化XML文件?

如何使用XSLT2.0将csv文件转换为结构化XML文件?,xml,csv,xslt,xslt-2.0,xslt-grouping,Xml,Csv,Xslt,Xslt 2.0,Xslt Grouping,我想将下面的CSV转换为XML 示例CSV输入 01,TeacherHeader1 02,StudentHeader1 03,SubjectHeader1 10,Grade1,Score99 10,Grade2,Score99 48,SubjectTrailer1 49,StudentTrailer1 02,StudentHeader2 03,SubjectHeader1 10,Grade1,Score50 10,Grade2,Score50 48,SubjectTrailer1 49,Stud

我想将下面的CSV转换为XML

示例CSV输入

01,TeacherHeader1
02,StudentHeader1
03,SubjectHeader1
10,Grade1,Score99
10,Grade2,Score99
48,SubjectTrailer1
49,StudentTrailer1
02,StudentHeader2
03,SubjectHeader1
10,Grade1,Score50
10,Grade2,Score50
48,SubjectTrailer1
49,StudentTrailer2
50,TeacherTrailer1
输出应该是

  <FileHeader> 
    <id>01</id>  
    <name>TeacherHeader1</name> 
  </FileHeader>  
  <GroupRecord> 
    <GroupHeader> 
      <id>02</id>  
      <name>StudentHeader1</name> 
    </GroupHeader>  
    <AccountRecord> 
      <AccountHeader> 
        <id>03</id>  
        <name>SubjectHeader1</name> 
      </AccountHeader>  
      <AccountDetails> 
        <Details> 
          <id>10</id>  
          <name>Grade1</name>  
          <value>Score99</value> 
        </Details>  
        <Details> 
          <id>10</id>  
          <name>Grade2</name>  
          <value>Score99</value> 
        </Details> 
      </AccountDetails>  
      <AccountTrailer> 
        <id>48</id>  
        <name>SubjectTrailer1</name> 
      </AccountTrailer> 
    </AccountRecord>  
    <GroupTrailer> 
      <id>49</id>  
      <name>StudentTrailer1</name> 
    </GroupTrailer> 
  </GroupRecord>  
  <GroupRecord> 
    <GroupHeader> 
      <id>02</id>  
      <name>StudentHeader2</name> 
    </GroupHeader>  
    <AccountRecord> 
      <AccountHeader> 
        <id>03</id>  
        <name>SubjectHeader1</name> 
      </AccountHeader>  
      <AccountDetails> 
        <Details> 
          <id>10</id>  
          <name>Grade1</name>  
          <value>Score99</value> 
        </Details>  
        <Details> 
          <id>10</id>  
          <name>Grade2</name>  
          <value>Score99</value> 
        </Details> 
      </AccountDetails>  
      <AccountTrailer> 
        <id>48</id>  
        <name>SubjectTrailer1</name> 
      </AccountTrailer> 
    </AccountRecord>  
    <GroupTrailer> 
      <id>49</id>  
      <name>StudentTrailer2</name> 
    </GroupTrailer> 
  </GroupRecord>  
  <FileTrailer> 
    <id>50</id>  
    <name>TeacherTrailer1</name> 
  </FileTrailer> 
我想将上面的CSV转换为如上所示的正确结构的XML。
任何帮助都将不胜感激。谢谢。

正如我在一篇评论中所说的,您可以使用
未解析文本
标记化
处理文本文件以将其转换为XML(或者在XSLT 3中使用
未解析文本行
标记化
,如果可用),然后可以使用嵌套的
xsl:for each group
完成其余任务,一旦建立了规则模式,甚至可能使用一个或两个递归函数;下面尝试为每个组s说明嵌套的

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    expand-text="yes"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:param name="data" as="xs:string">01,TeacherHeader1
02,StudentHeader1
03,SubjectHeader1
10,Grade1,Score99
10,Grade2,Score99
48,SubjectTrailer1
49,StudentTrailer1
02,StudentHeader2
03,SubjectHeader1
10,Grade1,Score50
10,Grade2,Score50
48,SubjectTrailer1
49,StudentTrailer2
50,TeacherTrailer1</xsl:param>

<xsl:param name="header-ids" as="xs:string*"
  select="'01', '02', '03', '10', '48', '49', '50'"/>

<xsl:param name="header-names" as="xs:string*"
  select="'FileHeader ', 'GroupHeader', 'AccountHeader', 'Details', 'AccountTrailer', 'GroupTrailer', 'FileTrailer'"/>

  <xsl:variable name="lines">
      <xsl:for-each select="tokenize($data, '\r?\n')">
          <line>
              <xsl:variable name="tokens" as="xs:string*" select="tokenize(., ',')"/>
              <id>{$tokens[1]}</id>
              <name>{$tokens[2]}</name>
              <xsl:if test="$tokens[3]">
                  <value>{$tokens[3]}</value>
              </xsl:if>
          </line>
      </xsl:for-each>
  </xsl:variable>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/" name="xsl:initial-template">
      <xsl:for-each-group select="$lines/line" group-starting-with="line[id = '01']">
          <File>
              <xsl:apply-templates select="."/>
              <xsl:for-each-group select="current-group() except ." group-ending-with="line[id = '50']">
                  <xsl:for-each-group select="current-group()[position() lt last()]" group-starting-with="line[id = '02']">
                      <GroupRecord>
                          <xsl:apply-templates select="."/>
                          <xsl:for-each-group select="current-group() except ." group-ending-with="line[id = '49']">
                              <xsl:for-each-group select="current-group()[position() lt last()]" group-starting-with="line[id = '03']">
                                  <AccountRecord>
                                      <xsl:apply-templates select="."/>
                                      <AccountDetails>
                                          <xsl:apply-templates select="(current-group() except .)[id != '48']"/>
                                      </AccountDetails>
                                      <xsl:apply-templates select="current-group()[id = '48']"/>
                                  </AccountRecord>
                              </xsl:for-each-group>
                              <xsl:apply-templates select="current-group()[last()]"/>
                          </xsl:for-each-group>
                      </GroupRecord>
                  </xsl:for-each-group>
                  <xsl:apply-templates select="current-group()[last()]"/>
              </xsl:for-each-group>
          </File>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="line">
      <xsl:element name="{$header-names[index-of($header-ids, current()/id)]}">
          <xsl:apply-templates/>
      </xsl:element>
  </xsl:template>

</xsl:stylesheet>

01,教师1
02,学生会主席1
03,主题标题1
一年级10分,99分
二年级10分,得99分
48,主题trailer 1
49、学生列车员1
02,学生会主席2
03,主题标题1
一年级10分,得50分
二年级10分,得50分
48,主题trailer 1
49、学生列车员2
50、教师培训1
{$tokens[1]}
{$tokens[2]}
{$tokens[3]}

. 为了示例的完整性和紧凑性,示例数据已经内联,但是您当然可以使用
。我还使用了
xsl:mode
声明和
name=“xsl:initial template”
,这两个XSLT 3特性都是XSLT 2处理器需要调整的,以阐明标识转换,并使用不同的模板名,例如
name=“main”
,作为代码的入口点。我还使用了文本值模板,比如
{$tokens[1]}
,对于XSLT2处理器,您需要使用
使用
未解析文本(-lines)
加上
标记化
,您应该能够将文本转换为XML。然后可以对每个组使用普通的
xsl:for
。我不确定您到底需要哪种方法,您想要的输出(由于其格式)似乎表明soem嵌套XML没有显示,您可能希望改进格式并删除XML不应该嵌套的任何缩进。感谢您的回复。我确实在我的帖子下面添加了一些图例,指出了每个记录标识符的标记(CSV上每行的前两个字符),并指出了它应该放置或分组的位置。您可以通过复制XML并使用外部编辑器来正确设置格式。希望有人能尽快帮助我:(CSV有
Score99
Score50
,为什么作为XML的输出只有
Score99
?我的错-这是复制粘贴的一个错误。谢谢Martin,我不得不删除XSLT 2不支持的所有函数for name=“xsl:initial template”我不必重复这一点,因为我们有一个标准的后端解决方案,可以将CSV转换为基本的XML。因此,此XSL的实际输入是一个简单的XML,每个元素包含CSV中的每一行(例如:01,TeacherHeader1 For xsl:mode,我还没有考虑如何将其转换为XSLT 2,但是这个解决方案基本上解决了我关于分组技术的问题(使用头/尾)非常感谢您的努力
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    expand-text="yes"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:param name="data" as="xs:string">01,TeacherHeader1
02,StudentHeader1
03,SubjectHeader1
10,Grade1,Score99
10,Grade2,Score99
48,SubjectTrailer1
49,StudentTrailer1
02,StudentHeader2
03,SubjectHeader1
10,Grade1,Score50
10,Grade2,Score50
48,SubjectTrailer1
49,StudentTrailer2
50,TeacherTrailer1</xsl:param>

<xsl:param name="header-ids" as="xs:string*"
  select="'01', '02', '03', '10', '48', '49', '50'"/>

<xsl:param name="header-names" as="xs:string*"
  select="'FileHeader ', 'GroupHeader', 'AccountHeader', 'Details', 'AccountTrailer', 'GroupTrailer', 'FileTrailer'"/>

  <xsl:variable name="lines">
      <xsl:for-each select="tokenize($data, '\r?\n')">
          <line>
              <xsl:variable name="tokens" as="xs:string*" select="tokenize(., ',')"/>
              <id>{$tokens[1]}</id>
              <name>{$tokens[2]}</name>
              <xsl:if test="$tokens[3]">
                  <value>{$tokens[3]}</value>
              </xsl:if>
          </line>
      </xsl:for-each>
  </xsl:variable>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/" name="xsl:initial-template">
      <xsl:for-each-group select="$lines/line" group-starting-with="line[id = '01']">
          <File>
              <xsl:apply-templates select="."/>
              <xsl:for-each-group select="current-group() except ." group-ending-with="line[id = '50']">
                  <xsl:for-each-group select="current-group()[position() lt last()]" group-starting-with="line[id = '02']">
                      <GroupRecord>
                          <xsl:apply-templates select="."/>
                          <xsl:for-each-group select="current-group() except ." group-ending-with="line[id = '49']">
                              <xsl:for-each-group select="current-group()[position() lt last()]" group-starting-with="line[id = '03']">
                                  <AccountRecord>
                                      <xsl:apply-templates select="."/>
                                      <AccountDetails>
                                          <xsl:apply-templates select="(current-group() except .)[id != '48']"/>
                                      </AccountDetails>
                                      <xsl:apply-templates select="current-group()[id = '48']"/>
                                  </AccountRecord>
                              </xsl:for-each-group>
                              <xsl:apply-templates select="current-group()[last()]"/>
                          </xsl:for-each-group>
                      </GroupRecord>
                  </xsl:for-each-group>
                  <xsl:apply-templates select="current-group()[last()]"/>
              </xsl:for-each-group>
          </File>
      </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="line">
      <xsl:element name="{$header-names[index-of($header-ids, current()/id)]}">
          <xsl:apply-templates/>
      </xsl:element>
  </xsl:template>

</xsl:stylesheet>