摘自:http://blog.sina.com.cn/s/blog_539f8fd6010005kf.html
作者两天忙的七荤八素的,为了HLC和 YG老兄的东东
正好在改写HLC的程序,涉及到EDI解析的一块,把些经验写下来。HLC的项目用到的是INVOIC,舱单用的是IFCSUM,都是EDIFACT规范。
首先,EDI是报文文件格式,文件有许多个
Segments
构成,Seg的结构就像“RFF+BM:HLCUSHA040203964”,Seg之间用单引号分隔。每个Seg又由Seg名和多个Composites
组成。Seg名由3个大写字母组成,像上面的例子,RFF就是Seg名。Seg名和Composites
间以及Composites
之间由加号分隔,上面的例子里BM:HLCUSHA040203964就是一个Composite。Composite由一个或多个Data
elements
组成,用冒号隔开。如果数据项里有分隔符存在,则用问号做表识。比如一个公司名称里带有单引号,那么这个单引号就要用?'来代替。+号和:号的情况也是一样的。
关于EDI的解析2
上一篇说的Segment及比其更小的结构,再说说Segment以上的结构。
EDIFACT里有多种报文规范,一般称为Message,每种报文规范对应于一种EDI应用,比如付款凭证是INVOIC,而舱单就是IFCSUM,VESDEP则是发船报文。Message——报文规范规定了一个报文文件的组成形式。整个的报文文件以UNB
Segment开头,由UNZ Segment结尾,中间则包含了1..n个Message。
每个Message都是以UNH Segment(Message
header)开头的,后面会紧跟着一个BGM Segment(Beginning of
Message),UNH说明报文规范版本,还会包含一个校验码,而BGM则主要用来说明Message
Type——报文规范的名称。比如如果是发票报文(INVOIC)则是BGM+380:::COMMERCIAL
INVOICE+30800295+9。这里的编号380和"COMMERCIAL
INVOICE"都说明了这是一个发票报文。30800295即1004
Composite则说明了发票的编号,最后9即1225
Composite说明了这是一个原始记录(还可以是更改记录、删除记录等等)。每个Message都以UNT
Segment结束,UNT Segment会包含开头的UNH
Segment里的校验码,还会有整个Message里的Segment条数。
BGM
和UNT之间所有的Segment用来说明报文内容。这些Segment可以是独立的,也可以多个Segment组成一个Segment
Group,用来表明某种比较复杂、无法由一个Segment说明的信息。
比如在IFCSUM报文规范里,在报文开头的部分,可以加入1..n个CNT
Segment来说明整个舱单中货物的总箱量、总毛重等等。然后是
0070
Segment group 1: RFF-DTM
这表明,在CNT
Segment后可以在报文里加上0..n个0070,这是个由一个RFF
Seg(必要)和一个DTM
Seg(不必要)组成的group。RFF用来说明提单信息,而DTM则是说明与提单有关的日期信息。
关于EDI的解析3
然后说说EDI实际应用和解析报文的情况
EDIFACT中的规范是很全的,由专门的委员会编撰,每半年出一次更改后的规范。但在实际应用中,一般都不需要那么全的规范,所以具体的使
用者(某个政府机构或者航运公司等等)会根据自己的需要出一个自己的规范,这个规范必然是EDIFACT的一个子集。这样做一般都是出于解析效率的考虑。
解析报文的方法——我没见过EDI解析的代码,只能说说自己做EDI解析的经验了。一般来说,一个Message解析完成后,其解析结果在数据库中会对应与主表中的一条记录,以及多个副表中的多条记录。主表——副表之间一般是由主表主键担任外键的形式相关联。
解析过程中还有个比较重要的问题是,解析过程中的异常处理。如果某个Message有错误,怎样定位错误,怎样处理这个Message或者是处理整个的报文文件。这些问题在一开始设计时都必须考虑完备。
EDI解析
我第一次做EDI解析时,完全是面向流程的,虽然这个Message很简单,但程序最后还是很难看。一堆的porcedure和function,隔了半年自己都看不懂了
然后做舱单的时候,由于是从delphi改到了java,而且自己对于EDI解析和OO的认识有了一点点的改观,于是又设计了一堆的类,虽然类的层次还是比较清楚,而且框架结构也比较清楚,对于扩展和修改都比较方便,但是总觉得有点不太对劲
我的设计是:有一系列有相同接口的Segment
parser类,负责Segment的解析;有几个结构处理类,处理Message和Segment
group以及相互之间关系;有几个builder类,根据Message和Segment
group创建对象;然后有多个adapter类处理与数据库之间的联系。
现在仔细的分析下发现的问题是:在各个类、包的责任上还不是划分的很合理。首先,在Segment层次上做基础的信息分析有点太小,Segment
parser包很容易大量增加。同时,Message的结构一旦很复杂,Message框架处理类内部结构会更加复杂。而Message框架处理类和builder类在功能上有点重叠。处理数据库的adapter层没有什么问题。
改动的预想:按照以责任—接口来进行类的设计的原则,对于某些不是很复杂的segment
or segment
group到实体对象的映射可以放在一个类中完成,而复杂的group的处理则由多个类合作完成。这样,类的层次就能够更加合理化。
比如,发票中的金额细目group。INVOIC规范里的1470,即Segment
group
38:ALC-ALI-DTM-SG39-SG40-SG41-SG42-SG43,是个两层结构的、由十几种Segment构成的复杂结构。实际情况是,只有ALC和MOA
segment一对一构成细目组。
ALC+C+DEST DELIVERY CHGE
MOA+282:740:USD
这样,group38的解析包可以直接调用字符串处理函数从报文中取出需要的信息——费目“DEST
DELIVERY
CHGE”,金额“740”,币种“USD”。这样少了10几个Segment处理类,少了5、6个结构处理类,只用一个builder类就可以完成比较
简单的金额细目处理。由于Segment处理层是在builder层下面,builder层上面的类直接调用buidler类的接口,不必知道底层的实
现。而一旦需求有更改,只要改动buidler类内部的实现方式就可以了。对builder类层上下都无影响。
阅读(1910) | 评论(0) | 转发(0) |