1. 程式人生 > >Vczh Library++3.0開發紀事之流式xml和json讀寫

Vczh Library++3.0開發紀事之流式xml和json讀寫

    每次完成一個任務的時候,都要看看有沒有什麼潛在的可以把功能是現成庫的地方。這十分有利於提高自己的水平。但至於你寫出來的庫會不會有人用,那是另一回事情了。

    這次為了完成一個多程式語言+多自然語言的文件編寫工具,不得不做一個可以一次生成一大批文字檔案的模板結構出來。有了模板必然有元資料,元資料必然是類似字串的東西,所以順手就支援了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, value10         Text,                    // value11         CData,                    // value12         Comment,                // value13 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開發紀事