制台见洋人,制台何其多

在《补8log一篇》中南桥回复说:

类似的例子只会越来越多.

其实这样的例子已经很多了,厦门DELL不过是其中一例罢了。有时候,我们不得不悲哀地感觉到,这个时代怎么跟清朝末年时有几分相似。

前些年电视上常说广东的外向型经济如何发展得好,外资企业多么的多,利税多么的大。现在不说了,改说长三角了。为什么呢?因为广东的很多出口加工型的劳动密集型外企都搬到如昆山这样的地方来了。据说是因为广东的免二减三到期了,换个地方又可以重新免二减三。

官员们怎么办?还能怎么办,当然是升官了,看人家前几年的”政绩”多么的好。现在?现在怎么样那是现任官员的事,当然他们肯定也会有自己的办法的。

BTW:看到一篇有意思的文章《网虫的绰号与签名文件》,这个作者的版权意识很强啊(见最后一句话)。^O^

[技术贴]继续说一下用DELPHI实现动态代理的问题

昨天的那个动态代理的UML类图还不够全面,在TFooInvHandler实现中并不是直接通过RTTI操作TFooImpl的,而是通过一个通用的RTTI Invoke实现类:TInvoker进行。FImpl作为TInvoker的构造子参数传入,TInvoker通过接口取得VTAB,然后进行Dispatch操作,这个实现是可以独立出来的通用操作。

这样的话,在Handler中的工作就很单纯了,就是实现Aspect。

昨天临下班前,令狐向我指出一个在编译语言中不好解决的问题:Invoke的Context。Aspect除了需要Invoke时的那些如接口,方法,参数,返回值等内容以外,还需要一些额外的内容,对于这些额外内容的处理将最终导致问题不可避免的复杂化。这对动态语言来说可能不成问题,但对于静态语言,这是一件可能导致过多不必要耦合的陷阱。

所以我昨天下班回去后又研究了一下。最后我想还是将问题尽可能地简单化比较好,Aspect所需要的Context还是由Aspect自己维护,DynamicProxy不提供这方面的义务,以尽可能避免不必要的侵入。

在研究完TInvContext的实现后,我决定还是就用它了,因为有现成的代码提供了对不同的调用约定的处理(比如Cdecl/Pascal/StdCall等),自己实现太麻烦了。但如果可能的话,我还是想同时提供一个转换机制,用于生成一个Variant动态数组的参数,以简化使用。

[技术贴]关于动态代理

本来想上周末没能用DELPHI实现动态代理就算了,可是这几天却始终放不下这个想法,这实在是一个太美妙的想法了。而且在认真看了VCL对SOAP的实现后,现在至少有九成的把握可以实现这样一个动态代理。

那么动态代理有什么用?

这要先从GoF的Proxy模式说起。

假设有下面这样一个接口及其实现:

现在,如果你是这个接口的用户,而这个接口及其实现者提供了一个:

Foo : IFoo;

给你,其中Foo指向TFooImpl的一个实例。现在你有了IFoo的定义,和这个Foo实例–注意,你没有TFooImpl的定义和实现代码。如果现在要求你为所有的IFoo.doSth增加事务功能(假设doSth被实现为对数据库作更新操作),要怎么办?

GoF的Proxy模式就是解决方案之一:

如果所示,首先要实现一个新的IFoo接口实现–TStaticProxy。其中用了一个属性FImpl记录了TFooImpl的实例。然后在TStaticProxy中实现doSth和bar,并且将不需要变更的bar函数直接委托给FImpl处理,而在doSth的实现里加入事务处理即可。TStaticProxy的代码大致如下:

TStaticProxy = class( TInterfacedObject, IIFoo )privateFImpl : IFoo;publicconstructor Create( aImpl : IFoo );function doSth( ... ) : xxx;function bar( ... ) : xxx;end;constructor TStaticProxy.Create( aImpl : IFoo );BeginFImpl := aImpl;End;function TStaticProxy.doSth( ... ) : xxx;BeginBeginTransaction;Result := FImpl.doSth( ... );EndTransaction;End;function TStaticProxy.bar( ... ) : xxx;BeginResult := FImpl.bar( ... );End;

然后,在所有需要用到Foo对象的地方,改用新的NewFoo对象,如下:

varNewFoo : IFoo;BeginNewFoo := TStaticProxy.Create( Foo ) As IFoo;...  //  之后就可以把NewFoo完全当作Foo一样使用了。End;

可见,我们通过了一个Proxy类代理了所有对IFoo接口的操作,相当于在从IFoo到TFooImpl之间插入了自己的代码,在某种程度上,这就是AOP所谓的“横切”。当然如果你能有TFooImpl的代码的话,就简单了,只要如下:

从TFooImpl派生一个TNewFooImpl,然后在其中Override一下TFooImpl中的doSth即可,然后就创建TNewFooImpl的实例来代替Foo引用即可。

但问题就在于你必须拥用TFooImpl的代码,并且可以变更所提供的Foo实例,但这在很多时候是做不到的–除非不是用DELPHI,而是如Python一类的动态语言^O^。比如组件容器,比如远程实例等。还有像“虚代理”(就是当创建FImpl代价很高时,就在创建时只创建代理类,然后在真正需要时才创建FImpl的实例)

但上面这种静态代理还是很麻烦。首先,如果IFoo的成员函数很多的话,必须要一一为它们加上代理实现;其次,如果在应用中有很多接口需要代理时,就必须一一为它们写这样的专用代理类;第三,需要变更代理功能时,需要修改所有的代理类……

特别是像组件容器或是通用远程代理这样,对要实现的接口并不能确定的情况下,静态代理一点用也没有。

所以我们需要“动态代理”。我是在看了GIGIX发表在今年第一期《程序员》上的《动态代理的前世今生》一文后,虽然他说是的JAVA在JDK1.3中提出的,在java.lang.reflect中的proxy。但这却让我突发奇想,发现其实完全可以在DELPHI里也实现这样一个动态代理。

一个典型的动态代理如下:

这样,我们只需要把要增加在功能做成一个IInvocationHandler接口的实例,如图中的TFooInvHandler。然后动态创建一个支持IFoo接口的TDynamicProxy的实例–它是一个动态代理,只需要传入相应的参数:要实现的接口和相应的InvHandler实例即可,不需要为每个接口写一个代理。当然如GIGIX文中所说,对于C++来说,这个可以用模板实现,但问题在于模板归根到底是一种编译时的动态化技术,对于组件容器这样需要运行时动态化的应用,它还是不能实现。最后,InvHandler通过RTTI去调用具体的实现Foo。

它的用法大致如下:

TFooInvHandler = class( TInterfacedObject, IInvocationHandler )privateFImpl : IFoo;publicconstructor Create( aImpl : IFoo );function Invoke( IID, MethodName, Args[] ) : xxx;end;constructor TFooInvHandler.Create( aImpl : IFoo );BeginFImpl := aImpl;End;function TFooInvHandler.Invoke( IID, MethodName, Args[] ) : xxxBeginIf ( IID = IFoo ) AND ( MethodName = 'doSth' ) ThenBeginBeginTransaction;Result := DoInvoke( FImpl, IID, MethodName, Args[] );EndTransaction;EndElseResult := DoInvoke( FImpl, IID, MethodName, Args[] );End;varHandler : IInvocationHandler;NewFoo : IFoo;BeginHandler := TFooInvHandler.Create( Foo );NewFoo := TDynamicProxy.Create( TypeInfo(IFoo), Handler ) As IFoo;...  //  之后就可以把NewFoo完全当作Foo一样使用了。End;

注意:其中IInvocationHandler接口我还没想好要怎么定义,所以这段代码只是大致说明一下问题。另外,其中的DoInvoke就是通过RTTI来调用FImpl的。

从上面的代码可以看到,TDynamicProxy通过参数IFoo动态生成了一个对IFoo接口的代理,并且通过Handler参数插入一个处理接口IInvocationHandler,由TDynamicProxy把对IFoo接口的调用全部转成对IInvocationHandler接口的调用,最后由TFooInvHandler类来视情况处理。在这里,可以通过运行时配置方式来动态决定是否需要切入事务所处理,需要对哪个接口的哪个方法切入。

有了这样一个动态代理,还可以很方便地在InvocationHandler里切入如安全性检查,LOG等。这样的话,用DELPHI来实现AOP也不成问题了。

现在我面临的问题就是:如何来定义这个IInvocationHandler。

其实这里最主要的问题就是参数的传递的问题。接口可以用IID表示,方法可以用方法名,但参数变化太多了:一是数量不确定,可以有任意多个参数;二是类型不确定;三是传值参数和引用参数的问题。如前面那个例子用的是简单的办法,就是用一个不定长的Variant数组来记录,可以解决前两个问题,但第三个问题就比较麻烦,难道要用一个Tuple来作返回值?太麻烦了吧。

在VCL的SOAP实现里是通过一个TInvContext在记录的,但这样的话对于Handler的开发者来说,就不得不面对TInvContext的内部复杂性,易用性太差。

这就是我现在还不能确定实现的那一成。-_-|||

猛禽 Feb.03-05

一件有意思的事

事情源于一个MOP贴子《★☆○●◇QQ群发现惊天大秘密(偶然发现)◇●○☆★》,我们几个在群里试了半天,发现果然如此,纳闷了半天。

后来我试了两三次后发现,这个问题跟最后一个“婷”字无关,于是明白乐。

除了这个人物以外,我又试了另外两个人的名字,果然都不行。呵呵。政治无处不在啊。^O^

随手加到天天网摘里,刚才再去看时发现,MOP上终于有人也明白是怎么回事了。看来这事真的是很久远了,MOP上的小P孩们大多都不知道的。

“神化”后的“胡话”并三谈Gullibility

貌似偶经常就一个问题要谈上三次,看来是越来越像3D乐。-_-|||

一早来就看到老方的一篇《鞭挞“老罗”和扬我中医威风!》。这个话题是我挑起来的,实在是对老罗(传说中的“罗永浩”)关于中医药的说法不敢苟同。

在《Gullibility》里有人回复说:

老罗说的就是事实,戳到了某些人的痛处而已。中药就是经验主义,古代不发达这样没问题,现在是要讲科学的。
如果中药真的是科学,就把你的起作用的成分列出来阿,把毒副重用列出来阿,有没有系统的分析?
伪科学就是伪科学

经验主义有什么不对的?对于未知的领域,经验是解决问题最直接的办法。现在的科学又能怎么样?癌症、艾滋病不也是不能治,西医最伟大的创举之一是发明了抗生素,但是现在造成的后果呢?现在的细菌比抗生素发明前强悍了N倍。

生命是一项奇迹,即使是用现代科学也有很多方面无法解释。

老罗说中药没有成分列表,没有列出毒副作用,你就都相信?Gullibility!!!

《本草纲目》不就是中药的成份表吗?李时珍对各种中药成份都作了总结记录,包括每种成份的药性(功用)及禁忌等都有说明,对于毒性也有说明,还有很多中药的配方都有考虑到各种成份之间的影响,以及通过合适的成份配比来降低毒性或使之相互抑制。对于药品的制备也有如炮制这样的手段来处理。根本不是老罗所谓的“便秘时想出来的方子”。

当然由于认识的局限性,肯定会有一些未知的副作用存在,但这不是反对中药的理由。发现了,记录下来,以后避免就是了。西药也一样,西药不也是要作人体实验的吗?这也是一种经验主义,跟中药没什么不同,何况中药比它有几千年的经验。

说一个大家都知道西药的副作用的例子:PPA。

在PPA被发现有副作用之前,它不是一样被当作感冒药广泛使用吗?吃死了多少人又有谁去管呢?

还是令狐说得对,老罗是个牛人,但不表示他什么方面都牛。

唉,Gullibility!

伊拉克民主的曙光

上周日的伊拉克大选终于落下帷幕。据说有72%的伊拉克人冒着被恐怖袭击的危险参与了投票。虽然计票结果还需要一段时间,但是现在的情况已经足已说明伊拉克人们的意愿。

在伊拉克的三个主要民族中,占人口数最多的什叶派穆斯林和北方希望独立的库尔德人对此次大选是充满热情的,而原来萨达姆所属的逊尼派穆斯林却大多表示抵制,据说是因为他们是少数民族,怕受迫害。还是那个杨小凯关于民主和共和的说法,不论是少数服从多数的民主,还是多数服从少数的共和,只要有服从,就有奴役。

对于现在的伊拉克人来说,最重要的事情就是赶快建立自己的政府,结束现在的被占领状态,并且希望能因此减少由此带来的反对占领的恐怖袭击事件–个人认为“反对占领”在很大程度上这只是恐怖份子的幌子,他们更主要的目的恐怕还是想趁火打劫,利用现在的混乱状况扩大自己的影响力,以武力进行争权夺利的斗争罢了。

像中东那样的地方,宗教对于政治有决定性的影响力,因此专制本来就是一个普遍的现象,伊拉克并非特例。只是希望这一次,伊拉克人民可以选出一个属于自己的政府–虽然在政治这个东东上,百姓永远都只能是政客们玩弄的工具罢了。

说到民主,偶想到今天看到的一则新闻:

云南洱海某村的村委会主任,在碰到一伙卖假药者后,将他们礼聘回村,并让他们假扮和尚,在村里的一座庙里坐镇解签骗钱。而村民们则在村头向游客宣传本村的寺庙的菩萨多么的灵,把他们骗到庙里去。每天晚上骗子们“下班”后与村里坐地分赃,双方居然还为此签定了“合同”。

据说国内的大部分行政村的村委会主任都是由村民直选的,算是最“民主”的一个职位了,看了这个新闻我相信了,至少这个村委会主任一定是得到全村百姓拥护的,因为他为本村带来了多么好的经济效益啊,真是为官一任,造福一方百姓啊。

相信这次选举出来的伊拉克新政府也一定可以为伊拉克人民带来滚滚的美元。

BTW:但还有一点小小的希望就是–类似上面那个村委会主任的方法还是不要用的好。^O^

尾牙、台湾并再谈Gullibility

帮主上周末去吃尾牙宴了,偶在那随便扯了几句关于尾牙的瞎话。后来考证了一下,是不对的。见《尾牙》和《台湾春节习俗拾趣》。显然这一风俗来自于闽南,而不是台湾的本土风俗。

说到这里,偶又想到老罗胡扯的关于台湾的那些话了。按老罗所说,偶就查了一下国内的资料《台湾历史》。

从进化论的角度上说,岛屿的生态环境太过于狭小,所以物种的进化不可避免地与大陆上有千丝万缕的联系。按地质资料来说,台湾在1.5万年前与大陆是连成一体的,这样看来,所谓的原住民,就应该是在此之前生活在台湾的原始人的后代。当然不排除之后的一万多年间陆续有少量的大陆人迁入。而老罗所谓的“狰狞的汉人”把台湾原住民赶到山上,然后管人家叫“高山族”的说法大约是指这个:

公元230年(三国吴黄龙二年),吴主孙权曾派将军卫温、诸葛直率领1万水军渡海到达台湾。

现在的台湾人,除了原住民以外,还有所谓的本省人,外省人和客家人。而在这其中,本省人主要讲闽南话(所谓的台语),占人口的约60%,讲客家话的客家人占到约20%,剩下约20%是讲其它方言的外省人和原住民。参见《台湾方言的故乡在中原》和《张克辉:警惕台湾有人利用语言文化分裂祖国》。这些人中的本省人是早年从福建迁过去的,按闽南话的起源来分析,闽南话是古汉语的一种,接近于唐代人的话。可见这些人是在唐代左右从中原迁入福建的。而客家人是在南宋时从中原迁到福建的,因为有500年左右的时间差,所以在人数上,闽南话人口比例要高于客家人。几千年来随着朝代更替,不断地有人从福建过去,使台湾的汉人数量逐渐增加。从语言上就可以看出汉人在台湾早已经占了绝对大多数–包括本省人、客家人和外省人(解放战争时随国民党军过台湾的全国各地的人)。

再从历史上看。除了上面说到的三国时吴国入侵台湾以外,后来的隋唐时期,台湾与琉球群岛主要是一种从属于中国的身份,直到元朝被并入中国的版图。明代开始对台湾的管理更加加强,出现大规模的汉人移民,特别是在明末郑芝龙(郑成功之父)等人的组织下开发台湾。1622年,荷兰殖民者入侵台湾,1626年,西班牙殖民者占领台湾北部,明朝政府内外交困无力顾及。明亡后,福建总兵郑芝龙降清,郑芝龙之子郑成功等人在福建等地扶持南明流亡政府,继续反抗满清。1661年,郑成功收复台湾,次年,荷兰殖民总督揆一签字投降。后郑成功及其子郑经以台湾为根据地,继续进行反清复明的斗争。后郑成功的部下施琅降清,并于1683年率军进攻台湾,郑成功之孙郑克爽归顺。之后再到1894年甲午中日战争中国战败后,1895年的马关条约将台(湾)澎(湖)金(门)马(祖)割给日本,直到1945年日本战败投降,日本在台湾实行了五十年的奴役统治。据统计在这五十年里,台湾人口大约被屠杀了六分之一,就像李敖说的,台湾就那么点大的地方,这些精英被杀光了,人民的精神就没有了。这也就是现在台独的基础。

其实台湾也挺惨的,虽然元朝时就并入中国,但明末清末时,政府一旦顾不上,就把台湾踢在一边不管。sigh~~~

当然所谓的“神圣不可侵犯”与“反清复明”这样的口号没什么区别,老罗也说得很实在,两岸争来争去都是汉人在争。从上面说的历史可见,现在的台湾人最大部分的人口都是明清时期迁入台湾的,所以他们的很多风俗习惯都与闽南地区相同。搞台独无非是统治阶级(都是汉人)为了自身利益考虑而已,哪里有什么可能是为了原住民。但就大陆自身利益考虑是无论如何不能让台湾独立的,要能让琉球独立那就更好乐。^O^

何况要说台湾是原住民的也未必就合适,且不说汉人在台湾生活了一千三百多年,而且其中大部分人口也至少在台湾生活了三百多年,而且要是从更远古说起,台湾也是大陆的一部分,原住民在原始时代也是中国人的一部分。

老罗在上课的时候为了活跃课堂气氛,瞎扯一些胡闹的内容无可厚非。但要是老罗说什么,都认为是真的话,那可是真是Gullibility了。也难怪他敢说,他要是一门心思搞邪教,可以搞比李红痔大十倍。

其实老罗不是个很诚实的人。

比如老罗为了证明他的RP比较好,拿了所谓的Emliy Dickinson的一首送给前男友的诗为证,但是很不幸被C彩旗考证出来说:

p.s.据坊间花边小道消息及不完全统计。偶亲爱滴荻更生同学。毕生。只有一个男友的说。

见《hospital》,我回复了句说:

C彩旗的小道消息告诉我们,老罗家里没有一棵樱桃树。-_-|||

听那段话的感觉,他们当时正在讲的题目便是与华盛顿家的樱桃树那个故事有关的,所以老罗才掰了这么些个故事的。

考证这种事虽然有点BT,但至少可以避免一些Gullibility的事情。^O^

BTW:本周第一8即告失败,因为C彩旗又补充道:

再次据坊间花边小道消息及不完全统计。偶亲爱滴荻更生同学。的确是被毕生仅有的这一个男友抛弃乐。~-_-||||

这么说老罗家里还是有樱桃树的,是偶考虑不周,灭想到有“前男友”并不表示还有“现男友”或“后男友”。-_-|||

还是麻烦

自从昨天搞定XML的持久化后,本想也试试在DELPHI里实现Dynamic Proxy–如果可以实现,那就意味着有可能可以DELPHI里实现AOP乐。结果发现难啊。

用COM的IDispatch倒是有点希望,但是这样就跟COM绑定在一起了,这是我所不希望的。

研究了一下DELPHI里的SOAP实现,用直接操作VTAB的方式可能可以,但是酱紫感觉比较不爽。

所以说,还是麻烦啊。

有时偶在想,偶是不是在干重新发明的轮子的事。-_-|||

这些东西在JAVA里都已经有了嘛。

不过话说回来,那些搞JAVA的人试图用Dynamic Proxy来实现AOP,感觉也是像是对maxin的一种模仿。

今天再想想办法,可以实现最好,不能实现就算了,已经几天没有8logging乐。^O^

BTW:目前的结果是–如果不用IDispatch的话,只能用VTAB。如朋友“太可怕”所说,MIDAS的SocketConnection就是基于IDispatch的。这个应该从Delphi 4就有了。没想到啊没想到。

BTW:看了一天RIO的源码,原来是用传说中的Thunk技术实现的,麻烦,看来要实现Dynamic Proxy虽然有可能,但还是很麻烦,留到过年时有空再研究吧。-_-

用DELPHI的RTTI实现对象的XML持久化

    去年我花了很多时间尝试用DELPHI进行基于XML的WEB应用开发。起初的设想是很美好的,但结果做出来的东西很简陋。一部分原因就在于XML到Object之间的数据绑定实现太麻烦(另一部分是因为对XSLT不熟,学习它花了很多时间)。

    之前我一直是用DELPHI提供的XML Data binding来做的,基本做法是:先用工具(如XMLSPY)做好一个XML Schema(XSD),然后用XML Data binding生成DELPHI的接口和类。当然,一旦生成好就很方便了,在程序里我只要操作这个接口就好了,其中各个Field都会被变成属性,并且类型也都如我在XSD中的定义。但问题在于程序在开发过程中,总是会有一些变化的,在这种情况下,我就不得不同时开着XMLSPY修改XSD,然后重新用XML Data binding的Wizard跑一遍,非常的麻烦。

    所以当我想到数据集的对象化后,立即想到也可以用RTTI来实现Object的XML持久化–其实DELPHI6开始的SOAP实现就是用RTTI来实现Object到SOAP数据(就是XML)的转换的。显然我已经是非常的后知后觉了,当我在《强大的DELPHI RTTI–兼谈需要了解多种开发语言》一文中说到我的打算时,朋友Lex CHow回复我说他在大约一年前就做过了这方面的工作,我当即跟他要来了他的源码。lexlib是他写的是一个有很多功能的库,看上去结构有点像.net的基本类库(当然没那么大^O^),Object的XML持久化只是其中的很小的一部分。因为我只需要这一部分,就没必要用这整个一个库这么麻烦,于是参考了lexlib并结合我在《用DELPHI的RTTI实现数据集的简单对象化》中已经实现的部分,做了一个简单的实现:

TMXMLPersistent = class(TObject)publicclass Procedure LoadObjFromXML( aNode : IXMLNode; aObj : TPersistent );class Procedure SaveObjToXML(   aNode : IXMLNode; aObj : TPersistent );end;constDefaultFilter : TTypeKinds = [tkInteger, tkChar, tkEnumeration,tkFloat, tkString, tkSet, tkWChar, tkLString, tkWString, tkInt64];{ TMXMLPersistent }class procedure TMXMLPersistent.LoadObjFromXML(aNode: IXMLNode;aObj: TPersistent);Vari : Integer;pList : TMPropList;pInfo : PPropInfo;tmpObj: TObject;beginIf ( aObj Is TMDataSetProxy ) Then( aObj As TMDataSetProxy ).LoadFromXML( aNode )ElseBeginpList := TMPropList.Create( aObj );TryFor i := 0 To pList.PropCount - 1 DoBeginpInfo := pList.Props[i];If ( pInfo^.PropType^.Kind = tkClass ) ThenBegintmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) ThenLoadObjFromXML( aNode.ChildNodes[WideString(pInfo^.Name)],tmpObj As TPersistent );EndElse If ( pInfo^.PropType^.Kind In DefaultFilter ) ThenSetPropValue( aObj, pInfo^.Name,String( aNode.ChildNodes[WideString( pInfo^.Name )].Text ) );End;FinallypList.Free;End;End;end;class procedure TMXMLPersistent.SaveObjToXML(aNode: IXMLNode;aObj: TPersistent);Vari : Integer;pList : TMPropList;pInfo : PPropInfo;tmpObj: TObject;beginIf ( aObj Is TMDataSetProxy ) Then( aObj As TMDataSetProxy ).SaveToXML( aNode )ElseBeginpList := TMPropList.Create( aObj );TryFor i := 0 To pList.PropCount - 1 DoBeginpInfo := pList.Props[i];If ( pInfo^.PropType^.Kind = tkClass ) ThenBegintmpObj := TObject( Integer( GetPropValue( aObj, pInfo^.Name ) ) );If ( Assigned( tmpObj ) AND ( tmpObj Is TPersistent ) ) ThenSaveObjToXML( aNode.AddChild( WideString( pInfo^.Name ) ),tmpObj As TPersistent );EndElse If ( pInfo^.PropType^.Kind In DefaultFilter ) ThenaNode.AddChild( WideString( pInfo^.Name ) ).Text :=GetPropValue( aObj, pInfo^.Name );End;FinallypList.Free;End;End;end;

    这个实现应该说是很简单的。主要是三个部分(Load和Save的结构是相似的):

    一是对TMDataSetProxy作特别处理,委托给这个类自己去处理它的实现,因为它与一般的类不同,需要通过ForEach遍历全部记录,这其实就是同时实现数据集的XML持久化。

    二是对Class作递归处理,当然只支持从TPersistent派生的class。

    三是一般的Field简单地转成String保存,其中借鉴了lexlib的Filter,只处理那些能简单地转成String的数据类型,过滤掉那些可能造成转换出错的类型。

    上面的代码中用到的TMPropList见《用DELPHI的RTTI实现数据集的简单对象化》中的实现。

    下面是用TMDataSetProxy实现的数据集的XML持久化。免去了需要通过TClientDataSet进行的麻烦,并且采用的是用Node记录字段的方式,.net也是采用这样的方式,与TClientDataSet所用的用Attribute记录字段的方式不同。虽然这样生成的XML文件将会略大一些,但是好处也是显而易见的,特别是我是准备用在Web应用中的,用Node方式记录的XML,在用XSLT时会方便很多。

procedure TMDataSetProxy.LoadFromXML(aNode: IXMLNode);Vari, j : Integer;pInfo : PPropInfo;pRow  : IXMLNode;beginFor j := 0 To aNode.ChildNodes.Count - 1 DoBeginFDataSet.Append;pRow := aNode.ChildNodes[j];For i := 0 To FPropList.PropCount - 1 DoBeginpInfo := FPropList.Props[i];If ( pInfo^.PropType^.Kind In DefaultFilter ) ThenSetVariant( i,String( pRow.ChildNodes[WideString( pInfo^.Name )].Text ) );End;EndEdit;End;FDataSet.First;end;procedure TMDataSetProxy.SaveToXML(aNode: IXMLNode);Vari : Integer;pInfo : PPropInfo;pRow  : IXMLNode;beginWhile ForEach DoBeginpRow := aNode.AddChild( 'Row' );For i := 0 To FPropList.PropCount - 1 DoBeginpInfo := FPropList.Props[i];If ( pInfo^.PropType^.Kind In DefaultFilter ) ThenpRow.AddChild( WideString( pInfo^.Name ) ).Text:= GetVariant( i );End;End;end;

    下面是一个简单的DEMO,其中包括了对数据集的XML持久化。注意Load的时候Employee成员连接的是ADODataSet2,它连接到一个包含了这几个字段的表,各字段类型与Employee表相同,但内容为空,并且去掉了EmployeeID的Identity。Load完成后,Employee表中这几个字段的内容将被复制到此表中。

TDemoCompany = class( TPersistent )privateFEmployee : TDSPEmployee;FCompany  : String;FCode     : Integer;publishedproperty Employee : TDSPEmployee Read FEmployee Write FEmployee;property Company  : String       Read FCompany  Write FCompany;Property Code     : Integer      Read FCode     Write FCode;End;procedure TForm1.SaveClick(Sender: TObject);Vardemo : TDemoCompany;begindemo := TDemoCompany.Create;demo.Employee := TDSPEmployee.Create( ADODataSet1 );demo.Company  := 'Demo company';demo.Code     := 987654;TryXMLDocument1.Active := true;TMXMLPersistent.SaveObjToXML( XMLDocument1.AddChild( 'Demo' ), demo );XMLDocument1.SaveToFile( 'temp.xml' );XMLDocument1.Active := false;Finallydemo.Employee.Free;demo.Employee := Nil;demo.Free;End;end;procedure TForm1.LoadClick(Sender: TObject);Vardemo : TDemoCompany;begindemo := TDemoCompany.Create;demo.Employee := TDSPEmployee.Create( ADODataSet2 );TryXMLDocument1.Active := true;XMLDocument1.LoadFromFile( 'temp.xml' );TMXMLPersistent.LoadObjFromXML( XMLDocument1.ChildNodes.Last, demo );XMLDocument1.Active := false;Edit1.Text := demo.Company;Edit2.Text := IntToStr( demo.Code );While ( demo.Employee.ForEach ) DoWith ListView1.Items.Add DoBeginCaption := IntToStr( demo.Employee.EmployeeID );SubItems.Add( demo.Employee.FirstName );SubItems.Add( demo.Employee.LastName );SubItems.Add( FormatDateTime( 'yyyy-mm-dd', demo.Employee.BirthDate ) );End;Finallydemo.Employee.Free;demo.Employee := Nil;demo.Free;End;end;

    终于可以告别那个麻烦的XML Data binding了,并且以后也不用写XSD了–虽然有好用的工具,但能省点事终归是好的。

猛禽 Jan.29-05