别人写了一个mircro XML解析器,附源代码
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: }
编码支持:
对于没有编码声明并且没有文件头签名文件,都视为 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: };
1: class XmlElement
3: virtual TiXmlElement* ToElement() { return this; } 4: };
5:
6: class XmlComment 7: {
8: virtual TiXmlComment* ToComment() { return this; } 9: };
我则借用了 COM 的 Queryinterface 的方式。在基类 XmlCastable 当中声明了一个虚函数 query,任意结点调用这个函数,输入一个类型,若该结点是这个类型,那么就输出指针的值并返回 true,否则输出 NULL 并返回 false。 1: class XmlCastable
3: virtual bool query( XmlType destType,void** ppvoid ) = 0; 5:
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: }
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 解析器剥离出来放上来。希望大家也都积极踊跃地共享代码。(注:还没有怎么详细测试,切不可用于重要的项目如国防工程火箭发射工程之类的,崩溃死机核弹爆炸什么的本人概不负责。。。。) 点击下载 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |