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 来实现字符集/编码的操作,因此对字符集/编码的支持很灵活,能够支持以上两个函数支持的所有编码,只需简单的修改即可添加新的支持。预置了几种编码支持:
- UTF-16LE(UNICODE)
- GBK
- BIG5
- GB2312
- UTF-7
- 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 声明一组虚函数
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@