前言:
前段時間做功能可行性的時候簡單的使用了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吧。
這里是頭文件的內容。

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 ] = { { "&", 5, '&' }, { "<", 4, '<' }, { ">", 4, '>' }, { """, 6, '\"' }, { "'", 6, '\'' } };
接下來就是說到的EncodeString了.

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

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: 讀取屬性中的值,讀取<></>之間的文本.

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:就是一個比較函數,比較簡單.

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:這是個轉化工具,我也不大懂.根據編碼規則應該就能實現.貼出來吧.

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對象基本的操作.如遍歷子節點,訪問兄弟節點,插入,刪除節點等操作.這樣在后面的子類繼承時,就不用考慮這些操作了.
先看看頭文件吧.

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

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,也調用了其他的函數

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

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

分析之后可以看出整體采用的就是雙向鏈表來組織數據的.其他的代碼就不用怎么展示了,大多都是鏈表的操作.
總結:
第一節主要就介紹兩個基類及其成員函數.整體顯得會比較枯燥,而且看完還是不明白整體tinyxml的處理方式.
詳細的tinyxml處理流程我將在第二節進行說明.有什么疑問歡迎交流.第一次寫這樣的文章,寫得不是很好,望大家多包涵.