xslt - 使用 XSLT 2.0 解析固定长度的文本文件
问题描述
我有一些固定长度的文本文件要处理,输出应该是结构化的 XML 文件。这些文本文件的格式在 XML 中指定。
棘手的部分是:
- 格式规范包含嵌套结构(一个
StructFormat
元素可以包含StructFormat
) - 结构可以重复(
repeat
例如使用 StructFormat 的属性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>
有人可以给我一些想法如何解决这个问题吗?
非常感谢!定军
解决方案
要在数字序列之外选择上下文节点的子元素,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
我不确定它们的含义以及如何将它们考虑到提取的正确位置。
推荐阅读
- numpy - 更快地将 Matplotlib 图转换为 Numpy 数组
- java - Java String#split() - 创建内存泄漏
- android - 如何在 Flutter 的地图上绘制从源到目的地的路径?
- javascript - 仅通过单击网页中的按钮显示隐藏 DIV 的网络抓取
- loops - 继承、增强循环和数组列表运行不正常
- ruby-on-rails - 如何在 ActionCable 频道中获取 request.session?
- wordpress - reCaptcha Invalid domain for site key key 错误
- amazon-web-services - 如何优化 AWS S3 存储桶的下载速度?
- php - 使用 twilio api php 发送传真
- php - 无法使用 PHP 和 MySQL 在浏览器上下载 CSV 文件