加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-16 05:12:23 所属栏目:百科 来源:网络整理
导读:mirco 的意思是比 tiny 还要 tiny。 GUI 模板用 XML 做是最合适的。方便嵌入脚本,方便编辑修改,方便嵌入皮肤描述,用 XML 做模板,写起 GUI 编辑器也要方便得多。 以前几个的 GUI 模板解析器用的是 MSXML 来实现的,不过它提供的接口字符串类型全是 BSTR,
mirco 的意思是比 tiny 还要 tiny。

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

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

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

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

测试代码:

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

编码支持:

  因为内部使用 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
   2: {
   3:     virtual TiXmlDocument*   ToDocument()    { return 0; }
   4:     virtual TiXmlElement*    ToElement()        { return 0; }
   5:     virtual TiXmlComment*    ToComment()     { return 0; }
   6:     virtual TiXmlUnknown*    ToUnknown()        { return 0; }
   7:     virtual TiXmlText*       ToText()        { return 0; }
   8:     virtual TiXmlDeclaration*    ToDeclaration() { return 0; }
   9: };


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

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


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

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

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


  如在 XmlNamedNode 当中:

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


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

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


类结构:

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


附件下载:

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

点击下载

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读