xml使用的还是比较多的,duilib界面也是通过xml配置实现的
duilib提供了CMarkkup和CMarkupNode类解析xml,使用起来也是比较方便的,比较好奇它是怎么实现的,如果自己来写一个
解析又需要怎样架构,架构之路还很遥远。。。
先来看看头文件吧,CMarkup主要是用于分割xml,判断xml格式是否正确;CMarkupNode主要是将CMarkup分割的xml,获取节点中的属性,
最多支持64个属性
1 enum 2 { 3 XMLFILE_ENCODING_UTF8 = 0,//定义编码,默认使用utf8 4 XMLFILE_ENCODING_UNICODE = 1, 5 XMLFILE_ENCODING_ASNI = 2, 6 }; 7 8 class CMarkup; 9 class CMarkupNode; 10 11 12 class UILIB_API CMarkup//其实比较容易看懂 13 { 14 friend class CMarkupNode; 15 public: 16 CMarkup(LPCTSTR pstrXML = NULL); 17 ~CMarkup(); 18 19 bool Load(LPCTSTR pstrXML); 20 bool LoadFromMem(BYTE* pByte, DWORD dwSize, int encoding = XMLFILE_ENCODING_UTF8);//从内存中加载解析 21 bool LoadFromFile(LPCTSTR pstrFilename, int encoding = XMLFILE_ENCODING_UTF8);//从文件中加载解析 22 void Release(); 23 bool IsValid() const; 24 25 void SetPreserveWhitespace(bool bPreserve = true); 26 void GetLastErrorMessage(LPTSTR pstrMessage, SIZE_T cchMax) const; 27 void GetLastErrorLocation(LPTSTR pstrSource, SIZE_T cchMax) const; 28 29 CMarkupNode GetRoot(); 30 31 private: 32 typedef struct tagXMLELEMENT 33 { 34 ULONG iStart;//节点开始指针位置 35 ULONG iChild;//第一个孩子节点指针位置 36 ULONG iNext;//下一个兄弟节点指针位置 37 ULONG iParent;//父亲节点指针位置 38 ULONG iData;//节点属性结束的位置 39 } XMLELEMENT; 40 41 LPTSTR m_pstrXML;//xml内存开始位置 42 XMLELEMENT* m_pElements;//数组储存节点信息 43 ULONG m_nElements; 44 ULONG m_nReservedElements; 45 TCHAR m_szErrorMsg[100]; 46 TCHAR m_szErrorXML[50]; 47 bool m_bPreserveWhitespace; 48 49 private: 50 bool _Parse(); 51 bool _Parse(LPTSTR& pstrText, ULONG iParent);//解析xml函数,主要是这里解析 52 XMLELEMENT* _ReserveElement();//如果数组不够长则扩充 53 inline void _SkipWhitespace(LPTSTR& pstr) const; 54 inline void _SkipWhitespace(LPCTSTR& pstr) const; 55 inline void _SkipIdentifier(LPTSTR& pstr) const; 56 inline void _SkipIdentifier(LPCTSTR& pstr) const; 57 bool _ParseData(LPTSTR& pstrText, LPTSTR& pstrData, char cEnd);//解析属性的值 58 void _ParseMetaChar(LPTSTR& pstrText, LPTSTR& pstrDest);//处理一些转义符号 59 bool _ParseAttributes(LPTSTR& pstrText);//解析属性 60 bool _Failed(LPCTSTR pstrError, LPCTSTR pstrLocation = NULL); 61 }; 62 63 64 class UILIB_API CMarkupNode 65 { 66 friend class CMarkup; 67 private: 68 CMarkupNode(); 69 CMarkupNode(CMarkup* pOwner, int iPos); 70 71 public: 72 bool IsValid() const; 73 74 CMarkupNode GetParent(); 75 CMarkupNode GetSibling(); 76 CMarkupNode GetChild(); 77 CMarkupNode GetChild(LPCTSTR pstrName); 78 79 bool HasSiblings() const; 80 bool HasChildren() const; 81 LPCTSTR GetName() const; 82 LPCTSTR GetValue() const;//这里获取不到节点的值,它返回的IData其实是属性末尾 83 84 bool HasAttributes(); 85 bool HasAttribute(LPCTSTR pstrName); 86 int GetAttributeCount(); 87 LPCTSTR GetAttributeName(int iIndex); 88 LPCTSTR GetAttributeValue(int iIndex); 89 LPCTSTR GetAttributeValue(LPCTSTR pstrName); 90 bool GetAttributeValue(int iIndex, LPTSTR pstrValue, SIZE_T cchMax); 91 bool GetAttributeValue(LPCTSTR pstrName, LPTSTR pstrValue, SIZE_T cchMax); 92 93 private: 94 void _MapAttributes();//将之前分割好的xml映射到m_aAttributes中储存起来 95 96 enum { MAX_XML_ATTRIBUTES = 64 }; 97 98 typedef struct 99 { 100 ULONG iName; 101 ULONG iValue; 102 } XMLATTRIBUTE; 103 104 int m_iPos; 105 int m_nAttributes; 106 XMLATTRIBUTE m_aAttributes[MAX_XML_ATTRIBUTES]; 107 CMarkup* m_pOwner;//指向CMarkup指针,节点属性及值都是从这里获取 108 };
简单说一下这两个类的工作原理,首先用CMarkup加载xml入内存,在将其分割字符串,建立节点树的关系(通过XMLELEMENT*指向节点位置关系,iStart指向节点开始部分,iData指向节点末尾,iChild,iNext,iParent储存节点之间的关系),获取节点是属性则通过CMarkupNode根据节点iStart,iData的值进行属性提取。CMarkupNode用于获取属性值及获取父亲,兄弟,儿子节点,熟悉了这两个类的作用,使用起来就比较方便了,具体实现下次再详细说明。