随笔-341  评论-2670  文章-0  trackbacks-0

    每次完成一个任务的时候,都要看看有没有什么潜在的可以把功能是现成库的地方。这十分有利于提高自己的水平。但至于你写出来的库会不会有人用,那是另一回事情了。

    这次为了完成一个多编程语言+多自然语言的文档编写工具,不得不做一个可以一次生成一大批文本文件的模板结构出来。有了模板必然有元数据,元数据必然是类似字符串的东西,所以顺手就支持了xml和json。为了应付巨大的xml和json,必然要做出xml和json的流式读写。大家应该听说过SAX吧,大概就是那样的。

    有了xml和json之后,就可以在上面实现query了。把xml和json统一起来是一个比较麻烦的问题,因为本身从结构上来看他们是不相容的。而且为了便于给vlscript.dll添加compiler service的支持,势必要在dll上面做出字符串到语法树或者语法树到字符串这样子的操作。本地dll接口显然不可能做出一个顺眼的异构树结构,因此势必要把语法树表示成xml或者json。因此就有了下面的结构:

    1、一颗简洁但是功能强大的字符串,在上面可以存放xml、json和强类型数据。
    2、可以把xml和树相互转换。
    3、可以把json和树相互转换。
    4、当树存放的是强类型数据(基本类型都会用字符串存放)的时候,树可以映射到一个带有约束的xml或者json结构上去。

    在这里可以展示一下什么是强类型数据。我们知道json表达的结构是弱类型的。当你拿到一个object的时候你不知道它的类型,你只知道他有多少个成员变量。强类型结构要求object有一个类型标记,数组也有一个类型标记(因为我们不能从数组的元素类型推断出数组本身的类型——因为类型可以拥有继承关系),基本类型还要可扩展(json只支持数字、字符串、布尔值和null,显然是不够的)。下面贴一个强类型数据结构的xml和json的表现形式:

 1 <Developer>
 2     <Languages>
 3         <array:String>
 4             <primitive:String value = "C++"/>
 5             <primitive:String value = "C#"/>
 6             <primitive:String value = "F#"/>
 7             <primitive:String value = "Haskell"/>
 8         </array:String>
 9     </Languages>
10     <Name>
11         <primitive:String value = "vczh"/>
12     </Name>
13     <Project>
14         <Project>
15             <Host>
16                 <primitive:String value = "Codeplex"/>
17             </Host>
18             <Language>
19                 <primitive:String value = "C++"/>
20             </Language>
21         </Project>
22     </Project>
23 </Developer>

 1 {
 2     "$object" : "Developer",
 3     "Languages" : {
 4         "$array" : "String",
 5         "value" : [{
 6             "$primitive" : "String",
 7             "value" : "C++"
 8         }, {
 9             "$primitive" : "String",
10             "value" : "C#"
11         }, {
12             "$primitive" : "String",
13             "value" : "F#"
14         }, {
15             "$primitive" : "String",
16             "value" : "Haskell"
17         }]
18     },
19     "Name" : {
20         "$primitive" : "String",
21         "value" : "vczh"
22     },
23     "Project" : {
24         "$object" : "Project",
25         "Host" : {
26             "$primitive" : "String",
27             "value" : "Codeplex"
28         },
29         "Language" : {
30             "$primitive" : "String",
31             "value" : "C++"
32         }
33     }
34 }

    上面的xml和json都是对一个相同的强类型数据结构的表示。我们可以看到$array、$object和$primitive是用来区分他们的实际类型的。

    下面是流式xml和json的读写的接口。可以很容易的看出用这种方法来读写xml和json必须将代码做成一个超级复杂的状态机才可以。这里太长贴不下,如果大家有兴趣的话可以去Vczh Library++3.0下载最新代码并打开
    Library\Entity\TreeXml.cpp
    Library\Entity\TreeJson.cpp
    Library\Entity\TreeQuery.cpp
 1 class XmlReader
 2 {
 3 public:
 4     enum ComponentType
 5     {
 6         ElementHeadOpening,        // name
 7         ElementHeadClosing,        //
 8         ElementClosing,            //
 9         Attribute,                // name, value
10         Text,                    // value
11         CData,                    // value
12         Comment,                // value
13 
14         BeginOfFile,
15         EndOfFile,
16         WrongFormat,
17     };
18 public:
19     XmlReader(stream::TextReader& _reader);
20     ~XmlReader();
21 
22     ComponentType                       CurrentComponentType()const { return componentType; }
23     const WString&                      CurrentName()const { return name; }
24     const WString&                      CurrentValue()const { return value; }
25     bool                                Next();
26     bool                                IsAvailable()const { return componentType!=EndOfFile && componentType!=WrongFormat; }
27 };
28 
29 class XmlWriter
30 {
31 public:
32     XmlWriter(stream::TextWriter& _writer, bool _autoNewLine=trueconst WString& _space=L"    ");
33     ~XmlWriter();
34 
35     bool                                OpenElement(const WString& name);
36     bool                                CloseElement();
37     bool                                WriteElement(const WString& name, const WString& value);
38     bool                                WriteAttribute(const WString& name, const WString& value);
39     bool                                WriteText(const WString& value);
40     bool                                WriteCData(const WString& value);
41     bool                                WriteComment(const WString& value);
42 };
43 
44 class JsonReader
45 {
46 public:
47     enum ComponentType
48     {
49         ObjectOpening,
50         ObjectClosing,
51         Field,
52         ArrayOpening,
53         ArrayClosing,
54         Bool,
55         Int,
56         Double,
57         String,
58         Null,
59 
60         BeginOfFile,
61         EndOfFile,
62         WrongFormat,
63     };
64 public:
65     JsonReader(stream::TextReader& _reader);
66     ~JsonReader();
67 
68     ComponentType                       CurrentComponentType()const { return componentType; }
69     const WString&                      CurrentValue()const { return value; }
70     bool                                Next();
71     bool                                IsAvailable()const { return componentType!=EndOfFile && componentType!=WrongFormat; }
72 };
73 
74 class JsonWriter
75 {
76 public:
77     JsonWriter(stream::TextWriter& _writer, bool _autoNewLine=trueconst WString& _space=L"    ");
78     ~JsonWriter();
79 
80     bool                                OpenObject();
81     bool                                CloseObject();
82     bool                                AddField(const WString& name);
83     bool                                OpenArray();
84     bool                                CloseArray();
85     bool                                WriteBool(bool value);
86     bool                                WriteInt(vint value);
87     bool                                WriteDouble(double value);
88     bool                                WriteString(const WString& value);
89     bool                                WriteNull();
90 };

    有xml自然要有xpath,只不过xpath用来处理json和强类型数据结构都有点力不从心,所以我打算修改xpath,做成适合查询我这种跟它们稍微有点不同的树的查询语句,然后加入到Vczh Library++3.0里面去。有了这个之后就可以做很多事情了,譬如说在模板生成器里面使用query来查询复杂的配置,譬如说在脚本语言里面支持xml和json,还有很多其他的等等。query写完之后就可以开始写一个可以一次生成一大批文本文件的模板生成器了。我会将模板生成本身写成一个可以扩展功能的库,最后再写一个DocWrite.exe利用这个库实现一个文档生成器。
posted on 2011-04-18 21:34 陈梓瀚(vczh) 阅读(3006) 评论(3)  编辑 收藏 引用 所属分类: VL++3.0开发纪事

评论:
# re: Vczh Library++3.0开发纪事之流式xml和json读写 2011-04-19 11:13 | dfdf
很好,很强大!  回复  更多评论
  
# re: Vczh Library++3.0开发纪事之流式xml和json读写 2011-04-19 23:02 | 溪流
顶顶更健康~!  回复  更多评论
  
# re: Vczh Library++3.0开发纪事之流式xml和json读写 2011-04-23 03:17 | 千暮(zblc)
留爪(2)  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理