tinyxml源碼解析(上)


前言:

  前段時間做功能可行性的時候簡單的使用了tinyxml。在sourceforge下載的時候,看到老外對於這個庫的評價很高,因此就有沖動進行學習這么一個優秀的框架。簡單查看了代碼,代碼量也不是很大。剛好事情做的差不多,索性就學習學習好了。第一次嘗試閱讀正式的框架,水平也不是很好,可能多少會有點出錯,要是有錯誤還望多包涵並且提出來,一起進步哈。

  文章打算分為三部分,第一部分介紹了TiXmlBase,TiXmlNode這兩個基類,第二部分詳細了介紹了代表節點的幾個類,第三部分介紹了庫中提供的一些工具類。

正言:

  先簡單介紹下tinyxml吧。在我的前一篇文章也介紹了下,還有使用方法。tinyxml是一款輕量級的xml parser。其目的在於在提供基本的xml操作的同時保持一個簡單的用戶接口供用戶使用。其能實現基本的xml的特性。如遍歷xml文件,獲取節點等。至於dtd,xstl等特性並不支持。詳細可以查看我的上一篇文章。

  tinyxml整體的類的架構圖如下:

 

  TiXmlBase是所有tinyxml里面的元素的基類,其提供了一些基本的操作和屬性。一會分析。

  xml中的節點的所有父類皆為TiXmlNode,其可以轉化成任意一種特定的節點。而對於我們的element節點中的屬性來說,其在構造時也是做為element的子節點來構建的,不過由於其只能出現在element中,因此並沒有繼承自TiXmlNode。

  TiXmlNode也提供了一些基本的節點操作,如獲取子節點,插入子節點等。

  這篇文章將主要講解TiXmlBase以及TiXmlNode。

  TiXmlBase:

  先講下所有類的基類TiXmlBase吧。

  這里是頭文件的內容。

View Code
  1 //TiXmlBase,所有的基類,提供了一些基本的操作和元素
  2 class TiXmlBase
  3 {
  4     friend class TiXmlNode;//這里使用friend讓人比較困惑,因為這三個都是子類,應該不用才是.在查看了tinyxmlparser.cpp的679行,主要是因為在函數中調用了做為參數的TiXmlNode的streamIn函數,而該函數是protected的,所以需要friend.當然肯定不止這么一個函數,應該用到了很多protected內的方法和參數.
  5     friend class TiXmlElement;//這邊就是同理了.
  6     friend class TiXmlDocument;
  7 
  8 public:
  9     TiXmlBase()    :    userData(0)        {}
 10     virtual ~TiXmlBase()            {}
 11 
 12     /**    Print是帶格式輸出,如果想不帶格式可以直接使用<<
 13     */
 14     virtual void Print( FILE* cfile, int depth ) const = 0;
 15 
 16     /**    由於xml中空格是否算元素還未定,因此作者留了個選項供用戶選擇
 17     */
 18     static void SetCondenseWhiteSpace( bool condense )        { condenseWhiteSpace = condense; }
 19 
 20     static bool IsWhiteSpaceCondensed()                        { return condenseWhiteSpace; }
 21 
 22     /** 返回的值為該元素在文件中的位置
 23          注意:增加或者刪除節點並不會改變位置,這個位置實際上實在load的時候確定的.
 24          同時,計算位置有一點的性能損失,可以選擇關閉
 25     */
 26     int Row() const            { return location.row + 1; }
 27     int Column() const        { return location.col + 1; }    ///< See Row()
 28 
 29     void  SetUserData( void* user )            { userData = user; }    ///< Set a pointer to arbitrary user data.
 30     void* GetUserData()                        { return userData; }    ///< Get a pointer to arbitrary user data.
 31     const void* GetUserData() const         { return userData; }    ///< Get a pointer to arbitrary user data.
 32 
 33     // 用來判斷UTF-8字符集字符長度的數組(1~4),具體定義在tinyxmlparser.cpp中
 34     static const int utf8ByteTable[256];
 35        //純虛函數,在子類中會重新定義,對不同節點進行不同處理,純虛函數也使得該類為虛基類,不可生成變量
 36     virtual const char* Parse(    const char* p, 
 37                                 TiXmlParsingData* data, 
 38                                 TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
 39 
 40     /** 工具函數,將節點值encode成相應的xml,其實就是對於<,>等xml特殊元素進行處理
 41     */
 42     static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
 43        //定義了一些轉化的時候的錯誤.
 44     enum
 45     {
 46         TIXML_NO_ERROR = 0,
 47         TIXML_ERROR,
 48         TIXML_ERROR_OPENING_FILE,
 49         TIXML_ERROR_PARSING_ELEMENT,
 50         TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
 51         TIXML_ERROR_READING_ELEMENT_VALUE,
 52         TIXML_ERROR_READING_ATTRIBUTES,
 53         TIXML_ERROR_PARSING_EMPTY,
 54         TIXML_ERROR_READING_END_TAG,
 55         TIXML_ERROR_PARSING_UNKNOWN,
 56         TIXML_ERROR_PARSING_COMMENT,
 57         TIXML_ERROR_PARSING_DECLARATION,
 58         TIXML_ERROR_DOCUMENT_EMPTY,
 59         TIXML_ERROR_EMBEDDED_NULL,
 60         TIXML_ERROR_PARSING_CDATA,
 61         TIXML_ERROR_DOCUMENT_TOP_ONLY,
 62 
 63         TIXML_ERROR_STRING_COUNT
 64     };
 65 
 66 protected:
 67        //工具函數,使之在處理文本的時候跳過空格.tinyxml中很多處理只針對english,latin,比如符號,字符等.不過通常情況下,這種處理結果還不錯
 68     static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
 69 
 70     inline static bool IsWhiteSpace( char c )        
 71     { 
 72         return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); 
 73     }
 74     inline static bool IsWhiteSpace( int c )
 75     {
 76         if ( c < 256 )
 77             return IsWhiteSpace( (char) c );
 78         return false;    // Again, only truly correct for English/Latin...but usually works.
 79     }
 80         //由於tinyxml對於一些不使用stl的用戶也提供了接口,因此這樣的宏很常見,也增加了不少接口.
 81     #ifdef TIXML_USE_STL
 82     static bool    StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
 83     static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
 84     #endif
 85 
 86     /*工具函數,用來讀取p中的名字,比如tagname,屬性名,屬性值等.
 87     */
 88     static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
 89 
 90     /*讀取in中的文本,即<> </>中的文本,
 91     */
 92     static const char* ReadText(    const char* in,                // where to start
 93                                     TIXML_STRING* text,            // the string read
 94                                     bool ignoreWhiteSpace,        // whether to keep the white space
 95                                     const char* endTag,            // what ends this text
 96                                     bool ignoreCase,            // whether to ignore case in the end tag
 97                                     TiXmlEncoding encoding );    // the current encoding
 98 
 99     // 處理xml中的實體,將<,>轉化回來,實現中用了些trick,之后展現
100     static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
101 
102     // 轉化一個char,如果是實體,同時進行轉化,在ReadText中被調用
103     inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
104     {
105         assert( p );
106         if ( encoding == TIXML_ENCODING_UTF8 )
107         {
108             *length = utf8ByteTable[ *((const unsigned char*)p) ];
109             assert( *length >= 0 && *length < 5 );
110         }
111         else
112         {
113             *length = 1;
114         }
115 
116         if ( *length == 1 )
117         {
118             if ( *p == '&' )
119                 return GetEntity( p, _value, length, encoding );
120             *_value = *p;
121             return p+1;
122         }
123         else if ( *length )
124         {
125             //strncpy( _value, p, *length );    // lots of compilers don't like this function (unsafe),
126                                                 // and the null terminator isn't needed
127             for( int i=0; p[i] && i<*length; ++i ) {
128                 _value[i] = p[i];
129             }
130             return p + (*length);
131         }
132         else
133         {
134             // Not valid text.
135             return 0;
136         }
137     }
138 
139     // 進行比較,ignoreCase只針對英文支持.
140     static bool StringEqual(    const char* p,
141                                 const char* endTag,
142                                 bool ignoreCase,
143                                 TiXmlEncoding encoding );
144 
145     static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
146     //TiXmlCursor 提供了位置信息,是一個包涵row,column的結構體
147     TiXmlCursor location;
148 
149     /// Field containing a generic user pointer
150     void*            userData;
151     
152     //三個函數只針對英文有用.工具函數
153     static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
154     static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
155     inline static int ToLower( int v, TiXmlEncoding encoding )
156     {
157         if ( encoding == TIXML_ENCODING_UTF8 )
158         {
159             if ( v < 128 ) return tolower( v );
160             return v;
161         }
162         else
163         {
164             return tolower( v );
165         }
166     }
167     //UTF32到UTF8的轉化函數,挺復雜的,我沒有研究,稍后展示.
168     static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
169 
170 private:
171     TiXmlBase( const TiXmlBase& );                //防止復制
172     void operator=( const TiXmlBase& base );    // 防止復制
173 
174     //定義了一些實體,在ReadText中用於轉化
175     struct Entity
176     {
177         const char*     str;
178         unsigned int    strLength;
179         char            chr;
180     };
181     enum
182     {
183         NUM_ENTITY = 5,
184         MAX_ENTITY_LENGTH = 6
185 
186     };
187     static Entity entity[ NUM_ENTITY ];//在tinyxmlparser.cpp中定義了.
188     static bool condenseWhiteSpace;//是否跳過空格
189 };

   讀完頭文件,來看看具體的實現

      首先是之前提到的entity,定義在tinyxmlparser.cpp

//定義了xml中的entity數組.用於函數轉化
TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = 
{
    { "&amp;",  5, '&' },
    { "&lt;",   4, '<' },
    { "&gt;",   4, '>' },
    { "&quot;", 6, '\"' },
    { "&apos;", 6, '\'' }
};

接下來就是說到的EncodeString了.

View Code
 1 //對xml中實體進行轉化,TIXML_STRING是作者封的一個宏.當定義了TIXML_USE_STL時,其即為string,否則就是作者自己實現的一個類.之后在講解工具類時會提到.
 2 void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
 3 {
 4     int i=0;
 5 
 6     while( i<(int)str.length() )
 7     {
 8         unsigned char c = (unsigned char) str[i];
 9 
10         if (    c == '&' 
11              && i < ( (int)str.length() - 2 )
12              && str[i+1] == '#'
13              && str[i+2] == 'x' )//為16進制的字符,直接復制.知道遇到';'
14         {
15             while ( i<(int)str.length()-1 )
16             {
17                 outString->append( str.c_str() + i, 1 );
18                 ++i;
19                 if ( str[i] == ';' )
20                     break;
21             }
22         }
23         //轉化字符為數組中的實體.
24         else if ( c == '&' )
25         {
26             outString->append( entity[0].str, entity[0].strLength );
27             ++i;
28         }
29         else if ( c == '<' )
30         {
31             outString->append( entity[1].str, entity[1].strLength );
32             ++i;
33         }
34         else if ( c == '>' )
35         {
36             outString->append( entity[2].str, entity[2].strLength );
37             ++i;
38         }
39         else if ( c == '\"' )
40         {
41             outString->append( entity[3].str, entity[3].strLength );
42             ++i;
43         }
44         else if ( c == '\'' )
45         {
46             outString->append( entity[4].str, entity[4].strLength );
47             ++i;
48         }
49         //是特殊不顯示字符,擴展
50         else if ( c < 32 )
51         {
52             // Easy pass at non-alpha/numeric/symbol
53             // Below 32 is symbolic.
54             char buf[ 32 ];
55             //取低8位進行轉化,TIXML_SNPRINTF是宏,
56             //在tinyxml.h 62行中定義,主要是為了解決編譯器兼容性問題
57             
58             #if defined(TIXML_SNPRINTF)        
59                 TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) );
60             #else
61                 sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
62             #endif        
63 
64             //*ME:    warning C4267: convert 'size_t' to 'int'
65             //*ME:    Int-Cast to make compiler happy ...
66             outString->append( buf, (int)strlen( buf ) );
67             ++i;
68         }
69         else
70         {
71             //直接復制
72             *outString += (char) c;    
73             ++i;
74         }
75     }
76 }

SkipWhiteSpace:這個實在沒什么好說的.

 1 //折疊又打不開了.就不折疊了
 2 const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
 3 {
 4     if ( !p || !*p )
 5     {
 6         return 0;
 7     }
 8     if ( encoding == TIXML_ENCODING_UTF8 )
 9     {
10         while ( *p )
11         {
12             const unsigned char* pU = (const unsigned char*)p;
13             
14             // 跳過ms的UTF8頭
15             if (    *(pU+0)==TIXML_UTF_LEAD_0
16                  && *(pU+1)==TIXML_UTF_LEAD_1 
17                  && *(pU+2)==TIXML_UTF_LEAD_2 )
18             {
19                 p += 3;
20                 continue;
21             }
22             else if(*(pU+0)==TIXML_UTF_LEAD_0
23                  && *(pU+1)==0xbfU
24                  && *(pU+2)==0xbeU )
25             {
26                 p += 3;
27                 continue;
28             }
29             else if(*(pU+0)==TIXML_UTF_LEAD_0
30                  && *(pU+1)==0xbfU
31                  && *(pU+2)==0xbfU )
32             {
33                 p += 3;
34                 continue;
35             }
36 
37             //仍然采用了英文的,ansi的方式進行判斷.
38             if ( IsWhiteSpace( *p ) )        
39                 ++p;
40             else
41                 break;
42         }
43     }
44     else
45     {
46         //對於其他編碼也是
47         while ( *p && IsWhiteSpace( *p ) )
48             ++p;
49     }
50 
51     return p;
52 }

 

ReadName

View Code
 1 //讀取element中的tagname和屬性的name,寫得很清晰,不用怎么解釋
 2 const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
 3 {
 4     *name = "";
 5     assert( p );
 6 
 7     if (    p && *p 
 8          && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
 9     {
10         const char* start = p;
11         while(        p && *p
12                 &&    (        IsAlphaNum( (unsigned char ) *p, encoding ) 
13                          || *p == '_'
14                          || *p == '-'
15                          || *p == '.'
16                          || *p == ':' ) )
17         {
18             //(*name) += *p; // while中的性能優化,將復制提到外頭去.
19             ++p;
20         }
21         if ( p-start > 0 ) {
22             name->assign( start, p-start );
23         }
24         return p;
25     }
26     return 0;
27 }

ReadText: 讀取屬性中的值,讀取<></>之間的文本.

View Code
 1 const char* TiXmlBase::ReadText(    const char* p,           //被讀的文本
 2                                     TIXML_STRING * text,     //讀取的字符
 3                                     bool trimWhiteSpace,     //是否要跳過空格
 4                                     const char* endTag,      //結束的tag,看起來比較別扭,不過用起來很方便
 5                                     bool caseInsensitive,    //判斷tag相等,是否關注大小寫
 6                                     TiXmlEncoding encoding )
 7 {
 8     *text = "";
 9     if (    !trimWhiteSpace            // certain tags always keep whitespace
10          || !condenseWhiteSpace )    
11     {
12         // Keep all the white space.
13         while (       p && *p
14                 && !StringEqual( p, endTag, caseInsensitive, encoding )
15               )
16         {
17             int len;
18             char cArr[4] = { 0, 0, 0, 0 };
19             p = GetChar( p, cArr, &len, encoding );//詳細看GetChar,這里取出的char已經是對實體進行了相應轉化了
20             text->append( cArr, len );
21         }
22     }
23     else //這里操作其實差不多,只是跳不跳過空格的區別.
24     {
25         bool whitespace = false;
26 
27         // Remove leading white space:
28         p = SkipWhiteSpace( p, encoding );
29         while (       p && *p
30                 && !StringEqual( p, endTag, caseInsensitive, encoding ) )
31         {
32             if ( *p == '\r' || *p == '\n' )
33             {
34                 whitespace = true;
35                 ++p;
36             }
37             else if ( IsWhiteSpace( *p ) )
38             {
39                 whitespace = true;
40                 ++p;
41             }
42             else
43             {
44                 // If we've found whitespace, add it before the
45                 // new character. Any whitespace just becomes a space.
46                 if ( whitespace )
47                 {
48                     (*text) += ' ';
49                     whitespace = false;
50                 }
51                 int len;
52                 char cArr[4] = { 0, 0, 0, 0 };
53                 p = GetChar( p, cArr, &len, encoding );
54                 if ( len == 1 )
55                     (*text) += cArr[0];    // more efficient
56                 else
57                     text->append( cArr, len );
58             }
59         }
60     }
61     if ( p && *p )
62         p += strlen( endTag );
63     return ( p && *p ) ? p : 0;
64 }

StringEqual:就是一個比較函數,比較簡單.

View Code
 1 bool TiXmlBase::StringEqual( const char* p,
 2                              const char* tag,
 3                              bool ignoreCase,
 4                              TiXmlEncoding encoding )
 5 {
 6     assert( p );
 7     assert( tag );
 8     if ( !p || !*p )
 9     {
10         assert( 0 );
11         return false;
12     }
13 
14     const char* q = p;
15 
16     if ( ignoreCase )
17     {
18         while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
19         {
20             ++q;
21             ++tag;
22         }
23 
24         if ( *tag == 0 )
25             return true;
26     }
27     else
28     {
29         while ( *q && *tag && *q == *tag )
30         {
31             ++q;
32             ++tag;
33         }
34 
35         if ( *tag == 0 )        // Have we found the end of the tag, and everything equal?
36             return true;
37     }
38     return false;
39 }

ConvertUTF32ToUTF8:這是個轉化工具,我也不大懂.根據編碼規則應該就能實現.貼出來吧.

View Code
 1 void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
 2 {
 3     const unsigned long BYTE_MASK = 0xBF;
 4     const unsigned long BYTE_MARK = 0x80;
 5     const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
 6 
 7     if (input < 0x80) 
 8         *length = 1;
 9     else if ( input < 0x800 )
10         *length = 2;
11     else if ( input < 0x10000 )
12         *length = 3;
13     else if ( input < 0x200000 )
14         *length = 4;
15     else
16         { *length = 0; return; }    // This code won't covert this correctly anyway.
17 
18     output += *length;
19 
20     // Scary scary fall throughs.
21     switch (*length) 
22     {
23         case 4:
24             --output; 
25             *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
26             input >>= 6;
27         case 3:
28             --output; 
29             *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
30             input >>= 6;
31         case 2:
32             --output; 
33             *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
34             input >>= 6;
35         case 1:
36             --output; 
37             *output = (char)(input | FIRST_BYTE_MARK[*length]);
38     }
39 }

 

   看完了最基礎的TiXmlBase類之后,也能感覺到,這個基類就是提供了基本的處理工具的作用,以及為后續的類設定了一些虛函數供后續繼承.而且由於是純虛類,因此也不能聲明TiXmlBase的對象.

  接下來就看看還具有一定實用性的TiXmlNode類吧.

  這個類是除了TiXmlAttribute之外的所有元素類的父類,里面提供了對於xml對象基本的操作.如遍歷子節點,訪問兄弟節點,插入,刪除節點等操作.這樣在后面的子類繼承時,就不用考慮這些操作了.

  先看看頭文件吧.

View Code
  1 /** The parent class for everything in the Document Object Model.
  2     (Except for attributes).
  3     Nodes have siblings, a parent, and children. A node can be
  4     in a document, or stand on its own. The type of a TiXmlNode
  5     can be queried, and it can be cast to its more defined type.
  6 */
  7 class TiXmlNode : public TiXmlBase
  8 {
  9     friend class TiXmlDocument;//這個原因就不用解釋了吧,還是和上頭一樣,需要訪問protected或者private的member function/property
 10     friend class TiXmlElement;
 11 
 12 public:
 13     //只有允許使用STL才有這兩個操作符
 14     #ifdef TIXML_USE_STL    
 15 
 16         /** 
 17         將結果輸入到base中,這三個操作符都是友元函數,定義在類的外頭.
 18         作者在注釋中提到了,operator>>允許空格和回車,內部處理機制更為復雜,之后我們可以看看.
 19         */
 20         friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
 21 
 22         /*
 23             這個輸出是不帶格式的.即不帶空格和回車,因此顯示的並不好看,不過計算機處理的時候也不用那么多好看的空格是吧.
 24         */
 25         friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
 26 
 27         //將元素的值增加到string后頭
 28         friend std::string& operator<< (std::string& out, const TiXmlNode& base );
 29 
 30     #endif
 31 
 32     /** 
 33         這就是node所支持的元素類型了,可以看到除了attribute都在了.不過attribute的實現方法可能和大家所想的不大一樣.我會在中篇的時候給大家展示一下.
 34         所有在處理過程中未識別的元素都將被認為是unknown.
 35     */
 36     enum NodeType
 37     {
 38         TINYXML_DOCUMENT,
 39         TINYXML_ELEMENT,
 40         TINYXML_COMMENT,
 41         TINYXML_UNKNOWN,
 42         TINYXML_TEXT,
 43         TINYXML_DECLARATION,
 44         TINYXML_TYPECOUNT
 45     };
 46 
 47     virtual ~TiXmlNode();//析構函數是虛函數,其目的在於以后在delete時,也能利用多態處理,調用真正的析構函數
 48 
 49     /** 
 50         Value對於不同的節點,其意義不盡相同.下面是各個類型的Value所代表的含義
 51         Document:    xml的文件名
 52         Element:    tagName
 53         Comment:    注釋的內容
 54         Unknown:    tag的內容
 55         Text:        text
 56     */
 57     const char *Value() const { return value.c_str (); }
 58 
 59     #ifdef TIXML_USE_STL
 60     // 只有stl才能使用的函數,注釋說明這種方式在實際使用中更為高效
 61     
 62     const std::string& ValueStr() const { return value; }
 63     #endif
 64 
 65     //在STL模式下,TIXML_STRING=string,在普通模式下為作者自己實現的string
 66     const TIXML_STRING& ValueTStr() const { return value; }
 67 
 68     /** 修改Value
 69            對應的意義和Value()函數想同
 70     */
 71     void SetValue(const char * _value) { value = _value;}
 72 
 73     #ifdef TIXML_USE_STL
 74     /// STL std::string form.
 75     void SetValue( const std::string& _value )    { value = _value; }
 76     #endif
 77 
 78     /// 刪除所有的子節點
 79     void Clear();
 80 
 81     /// 父節點,為了支持const類型,因此也包括了const的函數
 82     ///下面的很多函數都采用了這種寫法.我就列舉了Parent,其他刪掉了.不然好長..
 83     TiXmlNode* Parent()                            { return parent; }
 84     const TiXmlNode* Parent() const                { return parent; }
 85 
 86     TiXmlNode* FirstChild()                        { return firstChild; }//當沒有時返回Null
 87     const TiXmlNode* FirstChild( const char * value ) const;            ///第一個符合value的child,如果沒有返回null
 88     TiXmlNode* FirstChild( const char * _value ) {
 89         // 調用const的方法,因此先將this用const_cast轉化,再將返回值const_cast回來
 90         return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
 91     }
 92     TiXmlNode* LastChild()    { return lastChild; }/// 最后一個child,沒有則返回null
 93     //和上面的firstchild一樣的處理
 94     const TiXmlNode* LastChild( const char * value ) const;            
 95     TiXmlNode* LastChild( const char * _value ) {
 96         return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
 97     }
 98 
 99     //提供了string版本的FirstChild,LastChild,調用指針版本的結果
100     #ifdef TIXML_USE_STL
101     const TiXmlNode* FirstChild( const std::string& _value ) const    {    return FirstChild (_value.c_str ());    }
102     TiXmlNode* FirstChild( const std::string& _value )                {    return FirstChild (_value.c_str ());    }
103     const TiXmlNode* LastChild( const std::string& _value ) const    {    return LastChild (_value.c_str ());    }    
104     TiXmlNode* LastChild( const std::string& _value )                {    return LastChild (_value.c_str ());    }
105     #endif
106 
107     /** 
108         提供了遍歷子節點的方法.
109         previous可以為空.
110     */
111     const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
112     TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
113         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
114     }
115 
116     /// 迭代具有特殊value的所有子節點
117     const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
118     TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
119         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
120     }
121 
122     //stl版本.我就不展現了.
123 
124     /** 
125          在最后插入子節點.返回null如果有錯誤發生
126     */
127     TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
128 
129 
130     /**
131         這個函數與上個不同的就是對於新添加的addThis,其父節點變成了this(減少拷貝的考慮),因此在使用時需要特別注意.
132     */
133     TiXmlNode* LinkEndChild( TiXmlNode* addThis );
134 
135     /**
136         在某個節點前添加某個節點,返回新節點或者為NULL
137     */
138     TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
139 
140     /** 
141         在某個節點之后添加某個節點,返回新節點或者為NULL
142     */
143     TiXmlNode* InsertAfterChild(  TiXmlNode* afterThis, const TiXmlNode& addThis );
144 
145     /** 覆蓋某個節點
146     */
147     TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
148 
149     /// 移除某個節點
150     bool RemoveChild( TiXmlNode* removeThis );
151 
152     ///上一個兄弟節點
153     const TiXmlNode* PreviousSibling() const            { return prev; }
154     TiXmlNode* PreviousSibling()                        { return prev; }
155 
156     /// Navigate to a sibling node.
157     const TiXmlNode* PreviousSibling( const char * ) const;
158     TiXmlNode* PreviousSibling( const char *_prev ) {
159         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
160     }
161     
162     //STL模式,提供string的接口.
163 
164     ///下一個兄弟節點
165     TiXmlNode* NextSibling()                            { return next; }
166 
167     ///下一個具有特定值的兄弟節點
168     const TiXmlNode* NextSibling( const char * ) const;
169     TiXmlNode* NextSibling( const char* _next ) {
170         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
171     }
172 
173     /** 
174         返回下一個兄弟節點,同時是element類型.
175     */
176     const TiXmlElement* NextSiblingElement() const;
177     TiXmlElement* NextSiblingElement() {
178         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
179     }
180 
181     /**
182         返回下一個具有特定值的兄弟節點
183     */
184     const TiXmlElement* NextSiblingElement( const char * ) const;
185     TiXmlElement* NextSiblingElement( const char *_next ) {
186         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
187     }
188 
189     //stl模式參數string的接口
190 
191     ///用於遍歷是element類型的子節點
192     const TiXmlElement* FirstChildElement()    const;
193     TiXmlElement* FirstChildElement() {
194         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
195     }
196 
197     ///用於遍歷具有特定值且是element類型的子節點
198     const TiXmlElement* FirstChildElement( const char * _value ) const;
199     TiXmlElement* FirstChildElement( const char * _value ) {
200         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
201     }
202 
203     ///stl接口
204 
205     /// 返回該node的類型,用於轉化,進行進一步處理
206     
207     int Type() const    { return type; }
208 
209     //返回根
210     const TiXmlDocument* GetDocument() const;
211     TiXmlDocument* GetDocument() {
212         return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
213     }
214 
215     /// Returns true if this node has no children.
216     bool NoChildren() const                        { return !firstChild; }
217 
218     //轉化函數.這個設計的巧妙之處在於,子類也會相應繼承這個函數並且重寫相應的方法.
219     //對於不可轉化的類來說,其返回NULL.因此就避免了TiXmlElement轉化成TiXmlDocument.
220     virtual const TiXmlDocument*    ToDocument()    const { return 0; } 
221     virtual const TiXmlElement*     ToElement()     const { return 0; } 
222     virtual const TiXmlComment*     ToComment()     const { return 0; } 
223     virtual const TiXmlUnknown*     ToUnknown()     const { return 0; } 
224     virtual const TiXmlText*        ToText()        const { return 0; } 
225     virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } 
226 
227     virtual TiXmlDocument*          ToDocument()    { return 0; } 
228     virtual TiXmlElement*           ToElement()        { return 0; } 
229     virtual TiXmlComment*           ToComment()     { return 0; } 
230     virtual TiXmlUnknown*           ToUnknown()        { return 0; } 
231     virtual TiXmlText*                ToText()        { return 0; } 
232     virtual TiXmlDeclaration*       ToDeclaration() { return 0; } 
233 
234     ///克隆該節點,調用者必須手動刪除該節點.另外由於是純虛函數,因此,TiXmlNode也不能被構建
235     virtual TiXmlNode* Clone() const = 0;
236 
237     /** 
238         TiXmlVisitor是個工具類,Accept將調用該工具類進行輸出.之后將會講解
239     */
240     virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
241 
242 protected:
243     TiXmlNode( NodeType _type );
244 
245     //統一的復制函數
246     void CopyTo( TiXmlNode* target ) const;
247 
248     #ifdef TIXML_USE_STL
249         // The real work of the input operator.
250     virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
251     #endif
252 
253     // 判斷文本中接下來是什么類型的Node,以方便處理.在Parse中調用
254     TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
255 
256     TiXmlNode*        parent;
257     NodeType        type;
258 
259     TiXmlNode*        firstChild;
260     TiXmlNode*        lastChild;
261 
262     TIXML_STRING    value;
263 
264     TiXmlNode*        prev;
265     TiXmlNode*        next;
266 
267 private:
268     TiXmlNode( const TiXmlNode& );                // 未實現
269     void operator=( const TiXmlNode& base );    // 禁止,通過設置成private的方式來避免錯誤的復制.這種方式的設置可以在編譯的時候給予錯誤提示.
270 };

  在頭文件中,我們可以看到,TiXmlNode提供了很多遍歷,查找子節點的函數.為后續繼承的子類搭好了樹結構的基礎.同時通過私有化復制構造函數以及復制操作符的方式避免意外的復制從而導致錯誤以及額外的開銷.

  來看具體的實現.我們就挑幾個重要的函數看看好了.

  首先是遍歷子節點的操作IterateChildren

  

View Code
 1 const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
 2 {
 3     if ( !previous )
 4     {
 5         return FirstChild();
 6     }
 7     else
 8     {
 9         assert( previous->parent == this );
10         return previous->NextSibling();
11     }
12 }

  至於帶value版本的IterateChildren,也調用了其他的函數

View Code

  而帶value版本的FirstChild其實也沒什么神秘的,就是對鏈表的操作而已.

View Code
 1 const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
 2 {
 3     const TiXmlNode* node;
 4     for ( node = firstChild; node; node = node->next )
 5     {
 6         if ( strcmp( node->Value(), _value ) == 0 )
 7             return node;
 8     }
 9     return 0;
10 }

  那那些NextSibling等等其實也沒有什么好深究的了.還是看看變動性操作吧.

  LinkEndChild

View Code

  分析之后可以看出整體采用的就是雙向鏈表來組織數據的.其他的代碼就不用怎么展示了,大多都是鏈表的操作.

 

總結:

   第一節主要就介紹兩個基類及其成員函數.整體顯得會比較枯燥,而且看完還是不明白整體tinyxml的處理方式.

  詳細的tinyxml處理流程我將在第二節進行說明.有什么疑問歡迎交流.第一次寫這樣的文章,寫得不是很好,望大家多包涵.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM