BizTalk:为无分隔符位置式平面文件构建XSD
基于位置式平面文件构建BizTalk XSD实现XML转换
样例平面文件数据
0000000020220709200012000000 000000000 000000000 000000000 000000000 002029832022070922162607090111057992132 F 00210018202207091911210709033S061991911DS0649919111S067891911 F 9999999900000000000020000000 000000000 000000000 000000000 000000000
字段位置定义
| 列名 | 起始位置 | 结束位置 | 备注 |
|---|---|---|---|
| Area | 0 | 8 | 固定长度8位 |
| Date | 9 | 16 | 固定长度8位 |
| 行内记录数 | 27 | 28 | 决定该行包含多少个Change值,固定长度2位 |
| Change 1 | 31 | 35 | 固定长度5位,原文结束位置25为笔误,按样例数据修正 |
| Change 2 | 42 | 46 | 比Change 1偏移11位,固定长度5位 |
| Change 3 | 53 | 57 | 比Change 2偏移11位,固定长度5位 |
期望XML输出
<Lookup_Value_Set> <Record> <Area>00202983</Area> <Date>20220709</Date> <Change>05799</Change> </Record> <Record> <Area>00210018</Area> <Date>20220709</Date> <Change>06199</Change> </Record> <Record> <Area>00210018</Area> <Date>20220709</Date> <Change>06499</Change> </Record> </Lookup_Value_Set>
遇到的错误
Decoding of the flat file failed: 'Unexpected data found while looking for: '\n' The current definition being parsed is Lookup_Value_Set. The stream offset where the error occured is 75
环境信息
- 文件来自Linux系统,行分隔符为LF(
\n)而非CRLF - 每行固定100字符,数据仅填充至第73位
- 使用BizTalk命名空间
xmlns="http://schemas.microsoft.com/BizTalk/2003"
解决方案与XSD样例
核心解决要点
- 行分隔符配置:明确指定LF为行分隔符,同时按固定100字符长度定义行结构,避免数据未填满整行导致的匹配错误
- 首尾行跳过:通过
skip_first_rows和skip_last_rows属性直接跳过首尾行,无需额外筛选逻辑 - 可变Change字段处理:利用内嵌脚本将单行内的多个Change值拆分为独立的Record节点,匹配期望输出格式
完整XSD代码
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/BizTalk/2003" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="http://schemas.microsoft.com/BizTalk/2003/user" targetNamespace="http://schemas.microsoft.com/BizTalk/2003" attributeFormDefault="unqualified" elementFormDefault="qualified"> <xs:annotation> <xs:appinfo> <schemaEditorExtension:schemaInfo namespaceAlias="b" extensionClass="Microsoft.BizTalk.FlatFileExtension.FlatFileExtension" standardName="Flat File" xmlns:schemaEditorExtension="http://schemas.microsoft.com/BizTalk/2003/SchemaEditorExtensions"/> <b:schemaInfo standard="Flat File" codepage="65001" default_pad_char=" " pad_char_type="char" count_positions_by_byte="false" parser_optimization="speed" lookahead_depth="3" suppress_empty_nodes="false" generate_empty_nodes="true" allow_early_termination="false" early_terminate_optional_fields="false" allow_message_breakup_of_infix_root="false" compile_parse_tables="false" root_reference="Lookup_Value_Set"/> </xs:appinfo> </xs:annotation> <xs:element name="Lookup_Value_Set"> <xs:annotation> <xs:appinfo> <b:recordInfo structure="delimited" child_delimiter_type="hex" child_delimiter="0x0A" child_order="postfix" sequence_number="1" preserve_delimiter_for_empty_data="true" suppress_trailing_delimiters="false" skip_first_rows="1" skip_last_rows="1"/> </xs:appinfo> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="LineRecord"> <xs:annotation> <xs:appinfo> <b:recordInfo structure="positional" sequence_number="1" count_positions_by_byte="false"/> </xs:appinfo> </xs:annotation> <xs:complexType> <xs:sequence> <!-- Area字段:0-8位,长度8 --> <xs:element name="Area"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="8" sequence_number="1"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"> <xs:length value="8"/> </xs:restriction> </xs:simpleType> </xs:element> <!-- Date字段:9-16位,长度8 --> <xs:element name="Date"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="8" sequence_number="2"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"> <xs:length value="8"/> </xs:restriction> </xs:simpleType> </xs:element> <!-- 跳过18-26位,共9位 --> <xs:element name="Skip1"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="9" sequence_number="3" suppress_node="true"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"/> </xs:simpleType> </xs:element> <!-- 行内记录数字段:27-28位,长度2 --> <xs:element name="RecordCount" type="xs:int"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="2" sequence_number="4"/> </xs:appinfo> </xs:annotation> </xs:element> <!-- 跳过29-30位,共2位 --> <xs:element name="Skip2"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="2" sequence_number="5" suppress_node="true"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"/> </xs:simpleType> </xs:element> <!-- Change组:最多3个,每个占11位(5位值+6位跳过) --> <xs:element maxOccurs="3" name="ChangeGroup"> <xs:annotation> <xs:appinfo> <b:recordInfo structure="positional" sequence_number="6"/> </xs:appinfo> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="Change"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="5" sequence_number="1"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"> <xs:length value="5"/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="SkipChange"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="6" sequence_number="2" suppress_node="true"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"/> </xs:simpleType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <!-- 跳过剩余字符至100位,共42位 --> <xs:element name="SkipRest"> <xs:annotation> <xs:appinfo> <b:fieldInfo justification="left" pos_length="42" sequence_number="7" suppress_node="true"/> </xs:appinfo> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"/> </xs:simpleType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <!-- 内嵌脚本:将单行多Change拆分为独立Record节点 --> <xs:annotation> <xs:appinfo> <msxsl:script language="C#" implements-prefix="user"> <msxsl:assembly name="System.Xml"/> <msxsl:using namespace="System.Xml"/> public XmlNodeList SplitRecords(XmlNode lineRecord) { XmlDocument doc = new XmlDocument(); XmlElement root = doc.CreateElement("Records"); string area = lineRecord.SelectSingleNode("Area").InnerText; string date = lineRecord.SelectSingleNode("Date").InnerText; int count = int.Parse(lineRecord.SelectSingleNode("RecordCount").InnerText); XmlNodeList changes = lineRecord.SelectNodes("ChangeGroup/Change"); for(int i=0; i<count; i++) { XmlElement record = doc.CreateElement("Record"); // 构造Area节点 XmlElement areaEl = doc.CreateElement("Area"); areaEl.InnerText = area; // 构造Date节点 XmlElement dateEl = doc.CreateElement("Date"); dateEl.InnerText = date; // 构造Change节点 XmlElement changeEl = doc.CreateElement("Change"); changeEl.InnerText = changes[i].InnerText; record.AppendChild(areaEl); record.AppendChild(dateEl); record.AppendChild(changeEl); root.AppendChild(record); } return root.ChildNodes; } </msxsl:script> </xs:appinfo> </xs:annotation> </xs:schema>
关键配置说明
- 行分隔符与首尾行控制:在根节点
Lookup_Value_Set的recordInfo中,通过child_delimiter="0x0A"指定LF为行分隔符,skip_first_rows="1"和skip_last_rows="1"直接跳过首尾行 - 位置字段处理:所有字段通过
pos_length定义长度,无需保留的字段设置suppress_node="true",避免生成冗余XML节点 - 可变Record生成:内嵌C#脚本读取行内记录数,将每行的多个Change值拆分为独立的Record节点,完全匹配期望输出格式
- 固定行长度适配:最后设置
SkipRest字段填充剩余字符至100位,确保行分隔符匹配准确,解决解码时的换行符查找错误
调试建议
- 用十六进制编辑器确认文件行尾确实为
0x0A(LF) - 验证所有字段长度总和+跳过部分的长度等于100字符
- 先单独测试中间行数据,排除首尾行干扰后再整体验证
内容的提问来源于stack exchange,提问作者Ananda Prasad Bandaru
相关产品推荐
相关产品推荐

