使用XSLT 2.0解析固定长度的文本文件

时间:2018-11-15 13:39:40

标签: xslt

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

复杂的部分是:

  • 格式规范包含嵌套结构(一个StructFormat元素可以包含StructFormat
  • 可以重复结构(使用StructFormat的属性repeat,例如repeat='4')。

以下是格式规范的示例:

<?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>

有人可以给我一些解决方法的想法吗?

非常感谢! 鼎军

1 个答案:

答案 0 :(得分:0)

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

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

struct

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

<?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>

由于我不确定<?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> 等其他属性,我不确定它们的含义以及必须考虑到正确位置以提取它们的正确位置,因此我并没有对此加以考虑。