DuiLib 源码分析之解析xml类CMarkup & CMarkupNode 头文件

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用于获取属性值及获取父亲,兄弟,儿子节点,熟悉了这两个类的作用,使用起来就比较方便了,具体实现下次再详细说明。

原文地址:https://www.cnblogs.com/george-cw/p/5372135.html