别人写了一个mircro XML解析器,附源代码

前端之家收集整理的这篇文章主要介绍了别人写了一个mircro XML解析器,附源代码前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
mirco 的意思是比 tiny 还要 tiny。

  GUI 模板用 XML 做是最合适的。方便嵌入脚本,方便编辑修改,方便嵌入皮肤描述,用 XML 做模板,写起 GUI 编辑器也要方便得多。

  以前几个的 GUI 模板解析器用的是 MSXML 来实现的,不过它提供的接口字符串类型全是 BSTR,自己的接口又是 TCHAR*,每一次的调用都会有一次字符串转换,效率很低。而且一想到代码里有 QueryInteface,AddRef,Release ,心里就不踏实,担心吊胆的。

  于是又打算用 TinyXML,看了几遍代码,发现它对宽字符集的支持有些古怪。代码里其它的杂七杂八的东西有点多,代码风格什么的也不大喜欢。

  于是自己写了一个简单的,虽然有错误检测机制,不过没有提供查询接口,因为这样我觉得使接口不整洁。其实要知道 XML 哪里出错了很简单,随便拿个浏览器打开就行了,提示得非常详细。不过以后可能会根据需要添加上。XPATH 支持也是如此。接口函数全部使用 UNICODE。

测试代码

   1: // cexer@H_301_19@ 
  
   2: #include "./include/XML/xmlfile.h"@H_301_19@ 
  
   3: #include "./include/XML/xmlelement.h"@H_301_19@ 
  
   4: #include "./include/XML/xmldeclaration.h"@H_301_19@ 
  
   5: #include "./include/XML/xmlunknown.h"@H_301_19@ 
  
   6: #include "./include/XML/xmlcomment.h"@H_301_19@ 
  
   7: #include "./include/XML/xmltext.h"@H_301_19@ 
  
   8: #include "./include/XML/xmlattribute.h"@H_301_19@ 
  
   9: @H_301_19@ 
  
  10: using namespace cexer;@H_301_19@ 
  
  11: using namespace cexer::xml;@H_301_19@ 
  
  12: @H_301_19@ 
  
  13: @H_301_19@ 
  
  14: // c++ std@H_301_19@ 
  
  15: #include <iostream>@H_301_19@ 
  
  16: using namespace std;@H_301_19@ 
  
  17: @H_301_19@ 
  
  18: @H_301_19@ 
  
  19: int wmain( int argc,WCHAR** argv )@H_301_19@ 
  
  20: {@H_301_19@ 
  
  21:     wcout.imbue( std::locale("chs") );@H_301_19@ 
  
  22: @H_301_19@ 
  
  23:     XmlFile document( L"./XMLs/utf16le_ns.xml" );@H_301_19@ 
  
  24:     if ( !document.load() )@H_301_19@ 
  
  25:     {@H_301_19@ 
  
  26:         wcout<<L"解析失败"<<endl;@H_301_19@ 
  
  27:         return 0;@H_301_19@ 
  
  28:     }@H_301_19@ 
  
  29: @H_301_19@ 
  
  30:     XmlElement* root = document.element();@H_301_19@ 
  
  31:     if ( !root )@H_301_19@ 
  
  32:     {@H_301_19@ 
  
  33:         wcout<<L"没有找到根结点"<<endl;@H_301_19@ 
  
  34:         return 0;@H_301_19@ 
  
  35:     }@H_301_19@ 
  
  36: @H_301_19@ 
  
  37:     wcout<<root->name()<<endl;@H_301_19@ 
  
  38: @H_301_19@ 
  
  39: @H_301_19@ 
  
  40:     XmlNode* firstChild = root->firstChild();@H_301_19@ 
  
  41:     if ( !firstChild )@H_301_19@ 
  
  42:     {@H_301_19@ 
  
  43:         wcout<<L"没有子结点"<<endl;@H_301_19@ 
  
  44:         return 0;@H_301_19@ 
  
  45:     }@H_301_19@ 
  
  46: @H_301_19@ 
  
  47:     XmlDeclaration* declar = xml_cast<XmlDeclaration*>( firstChild );@H_301_19@ 
  
  48:     if ( !declar )@H_301_19@ 
  
  49:     {@H_301_19@ 
  
  50:         wcout<<L"第一个子结点不是声明"<<endl;@H_301_19@ 
  
  51:     }@H_301_19@ 
  
  52:     else@H_301_19@ 
  
@H_968_301@  53:     {@H_301_19@ 
  
  54:         wcout<<L"第一个节点是声明"<<endl;@H_301_19@ 
  
  55:         wcout<<L"version = "<<declar->version()<<endl;@H_301_19@ 
  
  56:         wcout<<L"encoding = "<<declar->encoding()<<endl;@H_301_19@ 
  
  57:         wcout<<L"standalone = "<<declar->standalone()<<endl;@H_301_19@ 
  
  58:     }@H_301_19@ 
  
  59: @H_301_19@ 
  
  60:     XmlComment* comment = xml_cast<XmlComment*>( firstChild->next() );@H_301_19@ 
  
  61:     if ( !comment )@H_301_19@ 
  
  62:     {@H_301_19@ 
  
  63:         wcout<<L"第二个子结点不是注释"<<endl;@H_301_19@ 
  
  64:     }@H_301_19@ 
  
  65:     else@H_301_19@ 
  
  66:     {@H_301_19@ 
  
  67:         wcout<<L"第二个结点是注释:"<<comment->value()<<endl;@H_301_19@ 
  
  68:     }@H_301_19@ 
  
  69: @H_301_19@ 
  
  70:     XmlElement* window = root->element( L"window" );@H_301_19@ 
  
  71:     if ( !window )@H_301_19@ 
  
  72:     {@H_301_19@ 
  
@H_801_403@  73:         wcout<<L"没有找到window元素结点"<<endl;@H_301_19@ 
  
  74:         return 0;@H_301_19@ 
  
  75:     }@H_301_19@ 
  
  76:     wcout<<window->attributeValue( L"text" )<<endl;@H_301_19@ 
  
  77: @H_301_19@ 
  
  78: @H_301_19@ 
  
  79:     XmlElement* nextWindow = window->nextElement( L"window" );@H_301_19@ 
  
  80:     if ( !nextWindow )@H_301_19@ 
  
  81:     {@H_301_19@ 
  
  82:         wcout<<L"没有找到后面的window元素结点"<<endl;@H_301_19@ 
  
  83:     }@H_301_19@ 
  
  84:     else@H_301_19@ 
  
  85:     {@H_301_19@ 
  
  86:         wcout<<L"后面还有一个window元素结点 ";@H_301_19@ 
  
  87:         wcout<<nextWindow->attributeValue(_T("name"))<<endl;@H_301_19@ 
  
  88:     }@H_301_19@ 
  
  89: @H_301_19@ 
  
  90:     XmlElement* panel = window->element( L"panel" );@H_301_19@ 
  
  91:     if ( panel )@H_301_19@ 
  
  92:     {@H_301_19@ 
  
  93:         wcout<<panel->attributeValue( L"caption" )<<endl;@H_301_19@ 
  
  94:     }@H_301_19@ 
  
  95: @H_301_19@ 
  
  96:     window->setAttribute( L"styleAdd",L"WS_VISIBLE" );@H_301_19@ 
  
  97:     window->setAttribute( L"styleRemove",L"\";<>=&\"" );@H_301_19@ 
  
  98: @H_301_19@ 
  
  99:     if ( !document.save( L"./XMLs/modified/utf16le_ns.xml" ) )@H_301_19@ 
  
 100:     {    @H_301_19@ 
  
 101:         wcout<<L"保存失败"<<endl;@H_301_19@ 
  
 102:     }@H_301_19@ 
  
 103: @H_301_19@ 
  
 104:     return 0;@H_301_19@ 
  
 105: }@H_301_19@ 
 

编码支持

  因为内部使用 MultiBytesToWideChar 和 WideCharToMultiBytes 来实现字符集/编码的操作,因此对字符集/编码的支持很灵活,能够支持以上两个函数支持的所有编码,只需简单的修改即可添加新的支持。预置了几种编码支持

  1. UTF-16LE(UNICODE)
  2. GBK
  3. BIG5
  4. GB2312
  5. UTF-7
  6. UTF-8

  对于没有编码声明并且没有文件头签名文件,都视为 UTF-8 编码。以上的代码当中的 XML 文件就是一个没有签名,没有声明的 UTF-16LE 编码的 XML 文件

  TinyXML 内部解析字符串全是以 char* 类型来解析,只支持多字节编码如 UTF-8,ASCII,不支持 UNICODE 编码。如对中文支持就比较古怪,如果 XML 以 UTF-8 格式保存,则设置“值”的时候必须自己把字符串转换为 UTF-8 再设置,而在取得值以后,则必须自己将它们从 UTF-8 转换成 UNICODE 或 ASCII,否则就是乱码。

  主要是 TinyXML 为了跨平台,所以没有像 MultiBytesToWideChar 和 WideCharToMultiBytes 这种函数的直接支持。不过如果是为了跨平台,这两个函数也可以考虑自己实现。

节点类型转换:

  因为在内存当中,元素,注释,文本,声明等结点都是存储为一个基类的指针,因此在使用的时候必须要有一个类型转换的动作。也有一些 XML 解析器比较简单,其中只有文档,元素,属性这三种结点,内存当中存的全部都是元素集合,元素当中再存有属性集合,用不着类型转换,不过这种 XML 将 XML 读入内存再存入磁盘文件当中时,会丢失掉 XML 文件原有的格式。

  TinyXML 在内存当中所有的结点(除属性)都以基类 TiXmlNode 指针的形式存放 ,从一个 TiXmlNode* 进行类型转的方法是这样的:

  首先在基 TiXmlBase 声明一组虚函数

1: class TiXmlNode@H_301_19@
   2: {@H_301_19@ 
  
   3:     virtual TiXmlDocument*   ToDocument()    { return 0; }@H_301_19@ 
  
   4:     virtual TiXmlElement*    ToElement()        { return 0; }@H_301_19@ 
  
   5:     virtual TiXmlComment*    ToComment()     { return 0; }@H_301_19@ 
  
   6:     virtual TiXmlUnknown*    ToUnknown()        { return 0; }@H_301_19@ 
  
   7:     virtual TiXmlText*       ToText()        { return 0; }@H_301_19@ 
  
   8:     virtual TiXmlDeclaration*    ToDeclaration() { return 0; }@H_301_19@ 
  
   9: };@H_301_19@ 
 


  然后在子类当中各自重写向自己类型转换的那一个虚函数。如在 XmlElement 和 XmlComment 当中:

1: class XmlElement@H_301_19@
   3:     virtual TiXmlElement*    ToElement()        { return this; }@H_301_19@ 
  
   4: };@H_301_19@ 
  
   5: @H_301_19@ 
  
   6: class XmlComment@H_301_19@ 
  
   7: {@H_301_19@ 
  
   8:     virtual TiXmlComment*    ToComment()     { return this; }@H_301_19@ 
  
   9: };@H_301_19@ 
 


  这样做很方便,不过如果要写一个新的 XML 结点类型,就比较麻烦了,必须重新修改基类 TiXmlNode 的代码,整个 XML 解析器的代码都得重新编译一遍。

  我则借用了 COM 的 Queryinterface 的方式。在基类 XmlCastable 当中声明了一个虚函数 query,任意结点调用这个函数,输入一个类型,若该结点是这个类型,那么就输出指针的值并返回 true,否则输出 NULL 并返回 false。

1: class XmlCastable@H_301_19@
   3:     virtual bool query( XmlType destType,void** ppvoid ) = 0;@H_301_19@ 
  
   5: @H_301_19@ 
 


  如在 XmlNamedNode 当中:

1: bool XmlNamedNode::query( XmlType type,void** pptr )@H_301_19@
   3:     if ( !pptr )@H_301_19@ 
  
   4:     {@H_301_19@ 
  
   5:         return false;@H_301_19@ 
  
   6:     }@H_301_19@ 
  
   7: @H_301_19@ 
  
   8:     *pptr = NULL;@H_301_19@ 
  
  10:     if ( XmlNamedNode::s_type == type )@H_301_19@ 
  
  11:     {@H_301_19@ 
  
  12:         *pptr = this;@H_301_19@ 
  
  13:         return true;@H_301_19@ 
  
  14:     }@H_301_19@ 
  
  15: @H_301_19@ 
  
  16:     return false;@H_301_19@ 
  
  17: }@H_301_19@ 
 


不过这样使用起来会相当地麻烦,所以写了一个辅助函数 xml_cast,可以对 XmlNode* 这样调用

1: XmlNode* node = ...;@H_301_19@
   2: XmlElement* element = xml_cast<XmlElement*>( node );@H_301_19@ 
  
   3: XmlComment* comment = xml_cast<XmlComment*>( node );@H_301_19@ 
 


类结构:

1: // 给函数 xml_cast 提供结点类型的转换能力@H_301_19@
   2: class XmlCastable@H_301_19@ 
  
   3: {...};@H_301_19@ 
  
   4: @H_301_19@ 
  
   5: // 除了属性结点(XmlAttribute)外,所有结点类型的基础@H_301_19@ 
  
   6: class XmlNode:public XmlCastable@H_301_19@ 
  
   7: {...};@H_301_19@ 
  
   8: @H_301_19@ 
  
   9: // 含有子结点的结点如:元素(XmlElement),文档(XmlDocument)@H_301_19@ 
  
  10: class XmlParentNode:public XmlNode@H_301_19@ 
  
  11: {...};@H_301_19@ 
  
  13: // 有名字的结点如:元素(XmlElement),属性(XmlAttribute)@H_301_19@ 
  
  14: class XmlNamedNode:public XmlCastable@H_301_19@ 
  
  15: {...};@H_301_19@ 
  
  16: @H_301_19@ 
  
  17: // 有值的结点如:属性(XmlAttribute),文本(XmlText),注释(XmlComment),未知(XmlUnknown)@H_301_19@ 
  
  18: class XmlValueNode:public XmlCastable@H_301_19@ 
  
  19: {...};@H_301_19@ 
  
  20: @H_301_19@ 
  
  21: @H_301_19@ 
  
  22: // XML声明 <?xml ..... ?>@H_301_19@ 
  
  23: class XmlDeclaration:public XmlNode@H_301_19@ 
  
  24: {...};@H_301_19@ 
  
  25: @H_301_19@ 
  
  26: @H_301_19@ 
  
  27: // XML注释结点<!-- ..... -->@H_301_19@ 
  
  28: class XmlComment:public XmlNode@H_301_19@ 
  
  29:,public XmlValueNode@H_301_19@ 
  
  30: {...};@H_301_19@ 
  
  31: @H_301_19@ 
  
  32: @H_301_19@ 
  
  33: // XML文本结点 [文本] 及 <![CDATA[...]]>@H_301_19@ 
  
  34: class XmlText:public XmlNode@H_301_19@ 
  
  35:,230)">  36: {...};@H_301_19@ 
  
  37: @H_301_19@ 
  
  38: // XML文档结点@H_301_19@ 
  
  39: class XmlDocument:public XmlParentNode@H_301_19@ 
  
  40: {...};@H_301_19@ 
  
  41: @H_301_19@ 
  
  42: // XML元素结点@H_301_19@ 
  
  43: class XmlElement:public XmlParentNode@H_301_19@ 
  
  44:,public XmlNamedNode@H_301_19@ 
  
  45: {...};@H_301_19@ 
  
  47: // XML元素属性@H_301_19@ 
  
  48: class XmlAttribute:public XmlNamedNode@H_301_19@ 
  
  49:,230)">  50: {...};@H_301_19@ 
  
  51: @H_301_19@ 
  
  52: // 解析器暂不支持的XML结点如@H_301_19@ 
  
@H_968_301@  53: //    <!DOCTYPE PLAY SYSTEM 'play.dtd'>@H_301_19@ 
  
  54: //    <!ELEMENT title (#PCDATA)>@H_301_19@ 
  
  55: //    <!ELEMENT books (title,authors)>@H_301_19@ 
  
  56: class XmlUnknown:public XmlNode@H_301_19@ 
  
  57:,230)">  58: {...};@H_301_19@ 
  
  60: @H_301_19@ 
 


附件下载:

  从 cexer 的库当中把 xml 解析器剥离出来放上来。希望大家也都积极踊跃地共享代码。(注:还没有怎么详细测试,切不可用于重要的项目如国防工程火箭发射工程之类的,崩溃死机核弹爆炸什么的本人概不负责。。。。)

点击下载

猜你在找的XML相关文章