如何使用 TinyXML 在內存中操作 xml 格式的內容


 如何使用 TinyXML 在內存中操作 xml 格式的內容

 

例子 xml 內容:
<?xml version="1.0" encoding="UTF-8" ?>
<Config>
    <Database ip="192.168.1.33" port="3306" />
    <List>
        <Channel count="5">電視劇</Channel>
        <Channel count="5">電影</Channel>
    </List>
</Config>


1. 分析一段保存在內存中的 xml 內容 (代碼見下方)

1) xml 內容中如果有中文,必須轉成 UTF-8格式,否則可能會出問題,比如此例中,"電視劇"的中文 gb2312 編碼會影響到后面的 "</Channel>",導致取這個節點的 text 時,得到的結果是: "電視劇</Channel>",而取下一個節點時將找不到節點。

2)使用 TiXmlDocument 與 TiXmlHandle 的區別: 一次取多級子節點元素時,當某一級節點不存在,用 doc (TiXmlDocument) 會出現異常,程序崩潰,而用 docHandle (TiXmlHandle) 則不會有異常。
比如:
    databaseElement = doc.FirstChildElement( "Conf" )->FirstChildElement( "Database" ); // 異常,崩潰
    databaseElement = docHandle.FirstChild( "Conf" ).FirstChild( "Database" ).ToElement(); // 不會異常,databaseElement 為 0
    databaseElement = docHandle.FirstChildElement( "Conf" ).FirstChildElement( "Database" ).ToElement(); // 不會異常,databaseElement 為 0
后兩種寫法的效果是一樣的。

以下為示例代碼,buffer 中保存着上面例子的 xml 內容:

[cpp]  view plain copy
  1. void CxmlDlg::ParseXML()  
  2. {  
  3.     char * buffer = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\  
  4. <Config>\  
  5.     <Database ip=\"192.168.1.33\" port=\"3306\" />\  
  6.     <List>\  
  7.         <Channel count=\"5\">電視劇</Channel>\  
  8.         <Channel count=\"5\">電影</Channel>\  
  9.     </List>\  
  10. </Config>";  
  11.   
  12.     char utf8[256] = {0};  
  13.   
  14.     if( MBSToUTF8( utf8, sizeof(utf8), buffer ) <= 0 ) // 此函數見我之前關於 TinyXML 的文章  
  15.         return;  
  16.   
  17.     TiXmlDocument doc;  
  18.     doc.Parse(utf8);  
  19.   
  20.     TiXmlElement* databaseElement = 0;  
  21.     TiXmlElement* listElement = 0;  
  22.     TiXmlElement* channelElement = 0;  
  23.   
  24.     TiXmlHandle docHandle( &doc );  
  25.     databaseElement = docHandle.FirstChild( "Config" ).FirstChild( "Database" ).ToElement();  
  26.     assert( databaseElement  );  
  27.   
  28.     // 取得字符串屬性的內容  
  29.     const char * ip = databaseElement->Attribute("ip");  
  30.   
  31.     // 取得整型屬性的值  
  32.     int port = 0;  
  33.     databaseElement->QueryIntAttribute("port", &port );  
  34.   
  35.     int count = 0;  
  36.     char content[32] = {0};  
  37.   
  38.     listElement = docHandle.FirstChild( "Config" ).FirstChild( "List" ).ToElement();  
  39.     assert( listElement );  
  40.     for( channelElement = listElement->FirstChildElement("Channel"); channelElement; channelElement = channelElement->NextSiblingElement("Channel") )  
  41.     {  
  42.         channelElement->QueryIntAttribute("count", &count );  
  43.         UTF8ToMBS( content, sizeof(content), channelElement->GetText() );  
  44.     }  
  45. }  


 

2. 合成一段 xml 內容保存在內存中 (代碼見下方)

有兩種寫法:
1) 第一種使用棧空間,聲明為局部變量,鏈接 TiXmlElement 的局部對象時必須用 Insert 系列的函數: InsertEndChild / InsertAfterChild / InsertBeforeChild,如果使用 LinkEndChild,在對象釋放時會有異常。原因正如 hoyt00 所說:"因為 Insert 系列的函數插入的是結點的副本(包括所有子結點),而 LinkEndChild 插入的就是你創建的對象。"

2) 第二種使用堆空間,動態分配對象,鏈接 TiXmlElement 時必須用 LinkEndChild 函數,這樣在最后 delete TiXmlDocument 對象時,TinyXML 內部才會幫你把動態生成的對象釋放掉。

// 參考了 hoyt00 的文章:http://blog.csdn.net/hoyt00/article/details/6769883:
     (1)TiXmlDocument對象最好在棧上創建, 如果在堆上創建了, 那你必須得自己銷毀它, 千萬不要像別的對象一樣new出了就不管了.
     (2)除了TiXmlDocument對象, 樹中的別的結點對象, 必須是堆上創建的, 千萬不要把棧上對象的地址鏈接(LinkEndChild)到樹中, 因為棧上對象是不能用delete銷毀的, 當然TinyXml也有對棧上對象插入的方法, 以下會說到.
     (3)除了文檔結點, new出的所有結點對象必須被鏈接到一個父親結點上, 才可以不用管對象的delete, 而且必須不能管, 不然有可能整棵樹會被破壞而得不到正確的遍歷和析構, 如果new出的對象從來沒鏈接到某棵樹上, 而且將來也不打算鏈接, 無論有沒有別的結點鏈接到它身上, 你都必須手動delete它.
     (4)不要嘗試鏈接已經被別的結點鏈接過的指針, 很顯然, 這會造成無法估量的麻煩, 不用多說, 大家都懂的.

 

寫法一:

[cpp]  view plain copy
  1. void CxmlDlg::MakeXML1()  
  2. {  
  3.     // 生成 XML 內容  
  4.     TiXmlDocument doc;  
  5.   
  6.     TiXmlElement config("Config");  
  7.   
  8.     TiXmlElement database("Database");  
  9.     database.SetAttribute( "ip""192.168.1.33" );  
  10.     database.SetAttribute( "port", 3306 );  
  11.   
  12.     config.InsertEndChild( database );  
  13.   
  14.     TiXmlElement list("List");  
  15.   
  16.     char utf8[32] = {0};  
  17.   
  18.     TiXmlElement channel1("Channel");  
  19.     channel1.SetAttribute( "count", 5 );  
  20.   
  21.     MBSToUTF8( utf8, sizeof(utf8), "電視劇" );  
  22.     TiXmlText text1( utf8 );  
  23.     channel1.InsertEndChild( text1 );  
  24.     list.InsertEndChild( channel1 );  
  25.   
  26.     TiXmlElement channel2("Channel");  
  27.     channel2.SetAttribute( "count", 5 );  
  28.   
  29.     MBSToUTF8( utf8, sizeof(utf8), "電影" );  
  30.     TiXmlText text2( utf8 );  
  31.     channel2.InsertEndChild( text2 );  
  32.     list.InsertEndChild( channel2 );  
  33.   
  34.     config.InsertEndChild( list );  
  35.     doc.InsertEndChild( config );  
  36.   
  37.     TiXmlPrinter printer;  
  38.     printer.SetIndent( 0 ); // 設置縮進字符,設為 0 表示不使用縮進。默認為 4個空格,也可設為'\t'  
  39.     doc.Accept( &printer );  
  40.   
  41.     char content[256] = {0};  
  42.     int size = printer.Size();  
  43.     assert( size < sizeof(content) );  
  44.     strcpy_s( content, sizeof(content), printer.CStr() );  
  45. }  


 寫法二:

[cpp]  view plain copy
  1. void CxmlDlg::MakeXML2()  
  2. {  
  3.     // 生成 XML 內容  
  4.     TiXmlDocument *doc = new TiXmlDocument();  
  5.   
  6.     TiXmlElement *config = new TiXmlElement("Config");  
  7.   
  8.     TiXmlElement *database = new TiXmlElement("Database");  
  9.     database->SetAttribute( "ip""192.168.1.33" );  
  10.     database->SetAttribute( "port", 3306 );  
  11.   
  12.     config->LinkEndChild( database );  
  13.   
  14.     TiXmlElement *list = new TiXmlElement("List");  
  15.   
  16.     char utf8[32] = {0};  
  17.   
  18.     TiXmlElement *channel1 = new TiXmlElement("Channel");  
  19.     channel1->SetAttribute( "count", 5 );  
  20.   
  21.     MBSToUTF8( utf8, sizeof(utf8), "電視劇" );  
  22.     TiXmlText *text1 = new TiXmlText( utf8 );  
  23.     channel1->LinkEndChild( text1 );  
  24.     list->LinkEndChild( channel1 );  
  25.   
  26.     TiXmlElement *channel2 = new TiXmlElement("Channel");  
  27.     channel2->SetAttribute( "count", 5 );  
  28.   
  29.     MBSToUTF8( utf8, sizeof(utf8), "電影" );  
  30.     TiXmlText *text2 = new TiXmlText( utf8 );  
  31.     channel2->LinkEndChild( text2 );  
  32.     list->LinkEndChild( channel2 );  
  33.   
  34.     config->LinkEndChild( list );  
  35.     doc->LinkEndChild( config );  
  36.   
  37.     TiXmlPrinter printer;  
  38.     printer.SetIndent( 0 );  
  39.     doc->Accept( &printer );  
  40.   
  41.     char content[512] = {0};  
  42.     int size = printer.Size();  
  43.     assert( size < sizeof(content) );  
  44.     memcpy( content, printer.CStr(), printer.Size() );  
  45.   
  46.     delete doc;  
  47. }  

 

3.TinyXml 保存時如何處理縮進和行結尾

1) 處理縮進:
a. 如果是保存文件,TinyXML 默認用4個空格做為縮進,根據節點的深度不同,縮進為4個空格的倍數,如果想去掉縮進,需要查找 TinyXML 源文件中所有寫4個空格的地方(一共是5處),注釋掉或換成你想要的縮進字符,也可以對 TinyXML 做一個改進,增加一個函數用於設置縮進字符,可以設為NULL,即無縮進。
b. 如果是保存到內存中,在 TiXmlPrinter 中有一個函數:SetIndent(),可以設置縮進字符,也可以設為 NULL。

2) 處理行結尾:
a. 如果是保存文件,TinyXML 會在每行的結尾用fprintf()寫一個 '\n',在 Windows 上,fprintf 寫入文件的 '\n' 都會被轉換為 "\r\n"。
b. 如果是保存到內存中,在 TiXmlPrinter 中有一個函數:SetLineBreak(),可以設置行結尾字符,也可以設為 NULL。


免責聲明!

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



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