首页 > 解决方案 > 使用 XSLT 2.0 解析固定长度的文本文件

问题描述

我有一些固定长度的文本文件要处理,输出应该是结构化的 XML 文件。这些文本文件的格式在 XML 中指定。

棘手的部分是:

以下是格式规范的示例:

<?xml version='1.0' encoding='windows-1252'?>
<MessageFormat name='NewMessageFormat' version='2.02'>
<FieldFormat name='G-TRA-MESSAGE-ID' type='String' delimOptional='y' length='18' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
    <StructFormat name='G-TAFT-HEAD-DAT' delimOptional='y'>
        <FieldFormat name='G-AFT-FIP-DATUM' type='String' delimOptional='y' length='10' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
        <FieldFormat name='G-AFT-FIP-ZEIT' type='String' delimOptional='y' length='8' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
    </StructFormat>
    <StructFormat name='G-TAFT-KUNDEN' delimOptional='y' repeat='2'>
        <FieldFormat name='G-AKU-LDSGVNR' type='String' delimOptional='y' length='6' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
        <FieldFormat name='G-AKU-KDSGVNR' type='String' delimOptional='y' length='10' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
    </StructFormat>
    <StructFormat name='G-TFGB-WGN-AN-LB' delimOptional='y' repeat='3'>
        <FieldFormat name='G-ZA-WGN-FUNK-CODE' type='String' delimOptional='y' length='1' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
        <FieldFormat name='G-FGB-WGN-RIV-CODE' type='String' delimOptional='y' length='2' strlenInChars='y' pad='0' padType='leading' trimLeading=' ' trimTrailing=' '/>
        <StructFormat name='G-FSB-SONDERBEH-CD' delimOptional='y' repeat='5'>
            <FieldFormat name='G-FSB-SONDERBEH-CD' type='String' delimOptional='y' length='2' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
        </StructFormat>
        <StructFormat name='G-TFGB-GUT-AN-LB' delimOptional='y' repeat='4'>
            <FieldFormat name='G-FGB-GGT-ZETT-NR-1' type='String' delimOptional='y' length='4' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
            <FieldFormat name='G-FGB-GGT-ZETT-NR-2' type='String' delimOptional='y' length='4' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
            <FieldFormat name='G-FGB-GGT-ZETT-NR-3' type='String' delimOptional='y' length='4' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
        </StructFormat>
        <FieldFormat name='G-FBD-PRIO-KENNZ' type='String' delimOptional='y' length='1' strlenInChars='y' trimLeading=' ' trimTrailing=' '/>
    </StructFormat>
    </MessageFormat>

还有一个文本示例:

G-TRA-MESSAGE-ID  G-AFT-FIP-G-AFT-FIG-AKU-G-AKU-KDSGG-AKU-G-AKU-KDSGGG-G-G-G-G-G-G-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGGGG-G-G-G-G-G-G-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGGGG-G-G-G-G-G-G-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG-FGG

所以预期的输出应该是:

<?xml version="1.0" encoding="UTF-8"?>
<NewMessageFormat>
<G-TRA-MESSAGE-ID>G-TRA-MESSAGE-ID</G-TRA-MESSAGE-ID>
<G-TAFT-HEAD-DAT>
    <G-AFT-FIP-DATUM>G-AFT-FIP-</G-AFT-FIP-DATUM>
    <G-AFT-FIP-ZEIT>G-AFT-FI</G-AFT-FIP-ZEIT>
</G-TAFT-HEAD-DAT>
<G-TAFT-KUNDEN>
    <G-AKU-LDSGVNR>G-AKU-</G-AKU-LDSGVNR>
    <G-AKU-KDSGVNR>G-AKU-KDSG</G-AKU-KDSGVNR>
</G-TAFT-KUNDEN>
<G-TAFT-KUNDEN>
    <G-AKU-LDSGVNR>G-AKU-</G-AKU-LDSGVNR>
    <G-AKU-KDSGVNR>G-AKU-KDSG</G-AKU-KDSGVNR>
</G-TAFT-KUNDEN>
<G-TFGB-WGN-AN-LB>
    <G-ZA-WGN-FUNK-CODE>G</G-ZA-WGN-FUNK-CODE>
    <G-FGB-WGN-RIV-CODE>G-</G-FGB-WGN-RIV-CODE>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-FBD-PRIO-KENNZ>G</G-FBD-PRIO-KENNZ>
</G-TFGB-WGN-AN-LB>
<G-TFGB-WGN-AN-LB>
    <G-ZA-WGN-FUNK-CODE>G</G-ZA-WGN-FUNK-CODE>
    <G-FGB-WGN-RIV-CODE>G-</G-FGB-WGN-RIV-CODE>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-FBD-PRIO-KENNZ>G</G-FBD-PRIO-KENNZ>
</G-TFGB-WGN-AN-LB>
<G-TFGB-WGN-AN-LB>
    <G-ZA-WGN-FUNK-CODE>G</G-ZA-WGN-FUNK-CODE>
    <G-FGB-WGN-RIV-CODE>G-</G-FGB-WGN-RIV-CODE>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>
    <G-FSB-SONDERBEH-CD>G-</G-FSB-SONDERBEH-CD>
    </G-FSB-SONDERBEH-CD>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-TFGB-GUT-AN-LB>
    <G-FGB-GGT-ZETT-NR-1>G-FG</G-FGB-GGT-ZETT-NR-1>
    <G-FGB-GGT-ZETT-NR-2>G-FG</G-FGB-GGT-ZETT-NR-2>
    <G-FGB-GGT-ZETT-NR-3>G-FG</G-FGB-GGT-ZETT-NR-3>
    </G-TFGB-GUT-AN-LB>
    <G-FBD-PRIO-KENNZ>G</G-FBD-PRIO-KENNZ>
</G-TFGB-WGN-AN-LB>
</NewMessageFormat>

我的方法是使用 XML 格式规范作为 XML 源,然后将文本作为字符串参数传递给 XLST 样式表,并使用内置的 XSLT 模板来处理每个元素节点。这是我的样式表:

<?xml version="1.0" encoding="UTF-8"?>
<?altova_samplexml file:///D:/Entwicklung/xslt/Test_MFL.xml?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="text"/>
<xsl:template match="/">
    <xsl:apply-templates select="element()"/>
</xsl:template>
<xsl:template name="StructFormat_withoutRepeat" match="StructFormat">
    <xsl:element name="{./@name}">
        <xsl:apply-templates select="element()"/>
    </xsl:element>
</xsl:template>
<xsl:template name="StructFormat" match="StructFormat[@repeat]">
    <xsl:variable name="repeat" select="./@repeat"/>
    <xsl:variable name="Name" select="current()/@name"/>
    <xsl:for-each select="1 to $repeat">
        <xsl:element name="{$Name}">
            <xsl:apply-templates select="element()"/>
        </xsl:element>
        <!--<xsl:call-template name="StructFormat_withoutRepeat"/>-->
    </xsl:for-each>
</xsl:template>
<xsl:template name="FieldFormat" match="FieldFormat">
    <xsl:variable name="fieldName" select="./@name"/>
    <xsl:element name="{./@name}">
        <xsl:value-of select="fn:substring($text,(/MessageFormat//FieldFormat[./@name = $fieldName]/preceding::FieldFormat/@length),  ./@length)"/>
    </xsl:element>
    <xsl:apply-templates select="element()"/>
</xsl:template>
</xsl:stylesheet>

但是它不适用于重复的结构。问题在于:

    <xsl:for-each select="1 to $repeat">
        <xsl:element name="{$Name}">
            <xsl:apply-templates select="element()"/>
        </xsl:element>
        <!--<xsl:call-template name="StructFormat_withoutRepeat"/>-->
    </xsl:for-each>

有人可以给我一些想法如何解决这个问题吗?

非常感谢!定军

标签: xslt

解决方案


要在数字序列之外选择上下文节点的子元素,for-each您需要将其存储在变量中,例如<xsl:variable name="this" select="."/>在 之前for-each,然后<xsl:apply-templates select="$this/element()"/>for-each.

为了处理重复的元素,我认为两步方法可以简化该任务,在第一步(在单独的模式下)我会将每个struct[@repeat]int 转换为相应数量的重复struct元素,然后我会处理该结果,然后它应该只是计算前面长度的任务:

<?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"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:param name="text" as="xs:string">1234512345123451234512345123451234512345123451234512345</xsl:param>

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

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

  <xsl:template match="struct[@repeat]" mode="unroll">
      <xsl:variable name="this" select="."/>
      <xsl:for-each select="1 to @repeat">
          <xsl:copy select="$this">
              <xsl:apply-templates select="@* except @repeat, node()" mode="#current"/>
          </xsl:copy>
      </xsl:for-each>
  </xsl:template>

  <xsl:variable name="complete-struct">
      <xsl:apply-templates mode="unroll"/>
  </xsl:variable>

  <xsl:template match="/">
      <xsl:apply-templates select="$complete-struct/*"/>
  </xsl:template>

  <xsl:template match="struct">
      <xsl:element name="{@name}">
          <xsl:apply-templates/>
      </xsl:element>
  </xsl:template>

  <xsl:template match="field">
      <xsl:element name="{@name}">
          <xsl:value-of select="substring($text, 1 + sum(preceding::field/@length), @length)"/>
      </xsl:element>
  </xsl:template>

</xsl:stylesheet>

在线https://xsltfiddle.liberty-development.net/eiZQaGj/4使用 XSLT 3 简化结构,但在 XSLT 中也应该可行:http: //xsltransform.hikmatu.com/jyH9rLS

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="2.0">

  <xsl:param name="text" as="xs:string">1234512345123451234512345123451234512345123451234512345</xsl:param>

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

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

  <xsl:template match="struct[@repeat]" mode="unroll">
      <xsl:variable name="this" select="."/>
      <xsl:for-each select="1 to @repeat">
          <xsl:element name="{name($this)}" namespace="{namespace-uri($this)}">
              <xsl:apply-templates select="$this/(@* except @repeat, node())" mode="#current"/>
          </xsl:element>
      </xsl:for-each>
  </xsl:template>

  <xsl:variable name="complete-struct">
      <xsl:apply-templates mode="unroll"/>
  </xsl:variable>

  <xsl:template match="/">
      <xsl:apply-templates select="$complete-struct/*"/>
  </xsl:template>

  <xsl:template match="struct">
      <xsl:element name="{@name}">
          <xsl:apply-templates/>
      </xsl:element>
  </xsl:template>

  <xsl:template match="field">
      <xsl:element name="{@name}">
          <xsl:value-of select="substring($text, 1 + sum(preceding::field/@length), @length)"/>
      </xsl:element>
  </xsl:template>

</xsl:transform>

我还没有理解和考虑其他属性,比如delimOptional我不确定它们的含义以及如何将它们考虑到提取的正确位置。


推荐阅读