编程手记之ANSI C篇-(五)XMLDOC
文档技术发展至今,Lotus Notes, MS Office, Adobe PDF, WPS 等,无不红极一时,各霸一方,但谁又能执其牛耳呢?如果说传统的文档是数据加样式的二合体的话,那么样式是牢笼,他将数据紧紧地束缚于其中,甲方乙方样式各自表征,死不相往来,在企业的数据海洋中垒起一个个信息孤岛,客户只是这些孤岛中的一个茫然的游客。但XML文档之横空出世仿佛改变了一切,成为当前业界最振奋人心之亮点。XML文档最朴实之处在于他首先关注的是数据,并将之描述成规范的表达,数据成为XML文档中的灵魂,至于样式,他通过外部的XML模式引入,这样客户占据了数据上的主动,并且拥有自由选择何种模式来表征数据的权利,岂不快哉!如果你对XML心仪,那么就赶快进入XML世界吧!
对于程序员来说,还是走离幻想的天空,踏踏实实地来写一段程序吧!前面已经实现了XML解析自动机,今天将在此基础上完成XML文档的访问接口-DOM。
DOM的结构实际上是一个二叉分析树的结构,DOM的节点由解析自动机在分析XML文档的过程中被逐一创建,XML文档分析请见《编程手记之ANSI C篇-(四)XML解析自动机》,以下来看一看DOM的接口功能实现:
/*用于格式化DOM节点属性时的缓冲结构*/
typedef struct _ProperBuf{
TCHAR* pBuf;
int size;
int max;
}ProperBuf;
/************************************************************
function: 创建DOM
return: DOM的连接件指针
*************************************************************/
LINKPTR XML_AllocDom(void)
{
/*使用二叉分析树作为DOM*/
return CreateTreeData();
}
/************************************************************
function: 销毁DOM
dom: DOM的连接件指针
*************************************************************/
void XML_FreeDom(LINKPTR dom)
{
DestroyTreeData(dom);
}
/************************************************************
functoin: 分析XML文档,生成DOM树
dom: DOM的连接件指针
szXML: XML文档字符串
return: 零值分析成功,非零值失败
*************************************************************/
int XML_Parse(LINKPTR dom,TCHAR* szXML)
{
XMLMac mac;
mac.token = szXML;
mac.act = paChild;
mac.parent = NULL;
mac.tree = dom;
/*自动机推演*/
_XMLParseTagEntity(&mac);
return mac.retcode;
}
/************************************************************
function: 取得DOM节点的子节点计数
ptrNode: 节点的连接件指针
return: 子节点的计数
*************************************************************/
int XML_ChildNodeCount(LINKPTR ptrNode)
{
return GetTreeDataChildItemCount(ptrNode);
}
/************************************************************
function: 取得DOM的根节点
ptrDom: DOM的连接件指针
return: 根节点的连接件指针,空树返回NULL
*************************************************************/
LINKPTR XML_GetRootNode(LINKPTR ptrDom)
{
return GetTreeDataRootItem(ptrDom);
}
/************************************************************
function: 取得第一个子节点
ptrNode: 节点的连接件指针
return: 子节点的连接件指针,没有子节点则返回NULL
*************************************************************/
LINKPTR XML_GetChildNode(LINKPTR ptrNode)
{
return GetTreeDataChildItem(ptrNode);
}
/************************************************************
function: 取得父节点
ptrNode: 节点的连接件指针
return: 父节点的连接件指针,根节点没有父节点,返回NULL
*************************************************************/
LINKPTR XML_GetParentNode(LINKPTR ptrNode)
{
return GetTreeDataParentItem(ptrNode);
}
/************************************************************
function: 取得后一个兄弟节点
ptrNode: 节点的连接件指针
return: 兄弟节点的连接件指针,没有后继节点则返回NULL
*************************************************************/
LINKPTR XML_GetNextSiblingNode(LINKPTR ptrNode)
{
return GetTreeDataNextSiblingItem(ptrNode);
}
/************************************************************
function: 取得前一个兄弟节点
ptrNode: 节点的连接件指针
return: 兄弟节点的连接件指针,没有前趋节点则返回NULL
*************************************************************/
LINKPTR XML_GetPrevSiblingNode(LINKPTR ptrNode)
{
return GetTreeDataPrevSiblingItem(ptrNode);
}
/************************************************************
function: 由节点连接件指针回溯DOM连接件指针
ptrNode: 节点的连接件指针
return: DOM的连接件指针
*************************************************************/
LINKPTR XML_DomFromNode(LINKPTR ptrNode)
{
return GetTreeDataFromItem(ptrNode);
}
/************************************************************
function: 取得节点的名称
ptrNode: 节点的连接件指针
szName: 字符缓冲区
max: 缓冲区大小
return: 实际拷贝的字符数
*************************************************************/
int XML_GetNodeName(LINKPTR ptrNode,TCHAR* szName,int max)
{
return ReadTreeDataItemProper(ptrNode,NODENAME,szName,max);
}
/************************************************************
function: 取得节点名称的字符串指针
ptrNode: 节点的连接件指针
return: 字符串指针
*************************************************************/
TCHAR* XML_GetNodeNamePtr(LINKPTR ptrNode)
{
return GetTreeDataItemProperPtr(ptrNode,NODENAME);
}
/************************************************************
function: 设置节点名称
ptrNode: 节点的连接件指针
szName: 名称字符串
*************************************************************/
void XML_SetNodeName(LINKPTR ptrNode,const TCHAR* szName)
{
WriteTreeDataItemProper(ptrNode,NODENAME,-1,(TCHAR*)szName,-1);
}
/************************************************************
functoin: 取得节点的文本字符串长度
ptrNode: 节点的连接件指针
return: 字符串长度值
*************************************************************/
int XML_GetNodeTextLength(LINKPTR ptrNode)
{
return GetTreeDataItemProperLength(ptrNode,NODETEXT);
}
/************************************************************
function: 取得节点的文本
ptrNode: 节点的连接件指针
szText: 字符串缓冲区
max: 缓冲区大小
return: 实际拷贝的字符数
*************************************************************/
int XML_GetNodeText(LINKPTR ptrNode,TCHAR* szText,int max)
{
return ReadTreeDataItemProper(ptrNode,NODETEXT,szText,max);
}
/************************************************************
function: 设置节点的文本
ptrNode: 节点的连接件指针
szText: 文本字符串
*************************************************************/
void XML_SetNodeText(LINKPTR ptrNode,const TCHAR* szText)
{
WriteTreeDataItemProper(ptrNode,NODETEXT,-1,(TCHAR*)szText,-1);
}
/************************************************************
function: 取得节点文本的字符串指针
ptrNode: 节点的连接件指针
return: 字符串指针
*************************************************************/
TCHAR* XML_GetNodeTextPtr(LINKPTR ptrNode)
{
return GetTreeDataItemProperPtr(ptrNode,NODETEXT);
}
/************************************************************
function: 取得节点属性值字符串指针
ptrNode: 节点的连接件指针
szKey: 属性名称
return: 属性值字符串指针
*************************************************************/
TCHAR* XML_GetNodeAttrPtr(LINKPTR ptrNode,const TCHAR* szKey)
{
return GetTreeDataItemProperPtr(ptrNode,szKey);
}
/************************************************************
function: 取得节点属性值字符串长度
ptrNode: 取得节点属性值字符串指针
szKey: 属性名称
return: 属性值字符串长度
*************************************************************/
int XML_GetNodeAttrLength(LINKPTR ptrNode,const TCHAR* szKey)
{
return GetTreeDataItemProperLength(ptrNode,szKey);
}
/************************************************************
function: 取得节点属性值字符串
ptrNode: 节点连接件指针
szKey: 属性名称
szVal: 字符串缓冲区
max: 缓冲区大小
return: 实际拷贝的字符数
*************************************************************/
int XML_GetNodeAttr(LINKPTR ptrNode,const TCHAR* szKey,TCHAR* szVal,int max)
{
return ReadTreeDataItemProper(ptrNode,szKey,szVal,max);
}
/************************************************************
function: 设置节点的属性值
ptrNode: 节点连接件指针
szKey: 属性名称
szVal: 属性值
*************************************************************/
void XML_SetNodeAttr(LINKPTR ptrNode,const TCHAR* szKey,const TCHAR* szVal)
{
WriteTreeDataItemProper(ptrNode,(TCHAR*)szKey,-1,(TCHAR*)szVal,-1);
}
/************************************************************
function: 插入一个新节点到DOM
ptrDom: DOM连接件指针
ptrNode: 父节点连接件指针
return: 新节点的连接件指针,如果父节点为NULL,则返回的新节点为根节点
*************************************************************/
LINKPTR XML_InsertNode(LINKPTR ptrDom,LINKPTR ptrNode)
{
LINKPTR node;
node = InsertTreeDataItem(ptrDom,ptrNode,LINK_LAST);
XML_SetNodeName(node,_T("NewNode"));
return node;
}
/************************************************************
function: 从DOM中删除一个节点
ptrDom: DOM连接件指针
ptrNode: 待删除的节点的连接件指针
*************************************************************/
void XML_DeleteNode(LINKPTR ptrDom,LINKPTR ptrNode)
{
DeleteTreeDataItem(ptrDom,ptrNode);
}
/************************************************************
function: 删除DOM中的所有节点
ptrDom: DOM连接件指针
*************************************************************/
void XML_DeleteAllNodes(LINKPTR ptrDom)
{
ClearTreeData(ptrDom);
}
/************************************************************
function: 回调函数,用于格式化节点的属性串
szName: 属性名称
namelen: 属性名称长度
szValue: 属性值
vallen: 属性值长度
param: 回调函数的回传参数,在此是一个ProperBuf的结构的指针
return: 零值格式化成功,非零值则出错
*************************************************************/
static int XML_FormatNodeProper(const TCHAR* szName,int namelen,const TCHAR* szValue,int vallen,void* param)
{
int len;
ProperBuf* pbi = (ProperBuf*)param;
/*skip NODENAME and NODETEXT*/
if(!_tcscmp(szName,NODENAME))
return 0;
else if(!_tcscmp(szName,NODETEXT))
return 0;
/*calc this property token length*/
len = namelen + vallen + _tcslen(_T("='' "));
if(pbi->size + len > pbi->max) /*buffer is overflow*/
{
return -1;
}
/*add property token to buffer end*/
if(pbi->pBuf != NULL)
_stprintf(pbi->pBuf + pbi->size,_T("%s=%c%s%c "),((szName)? szName : _T("")),_T('"'),((szValue)? szValue : _T("")),_T('"'));
pbi->size += len;
return 0;
}
/************************************************************
function: 格式化节点
ptrNode: 节点的连接件指针
pbi: 数据缓冲结构指针
return: 当前格式化入数据缓冲区的字节数
*************************************************************/
int XML_FormatNode(LINKPTR ptrNode,ProperBuf* pbi)
{
TCHAR* szName = NULL;
TCHAR* szText = NULL;
int len;
if(ptrNode == NULL)
return pbi->size;
while(ptrNode)
{
len = GetTreeDataItemProperLength(ptrNode,NODENAME);
if(pbi->size + len + (int)_tcslen(_T("< ")) > pbi->max)
return -1;
/*格式化节点首部*/
if(pbi->pBuf)
{
szName = (TCHAR*)calloc(len + 1,sizeof(TCHAR));
ReadTreeDataItemProper(ptrNode,NODENAME,szName,len);
_stprintf(pbi->pBuf + pbi->size,_T("<%s "),szName);
}
len = len + _tcslen(_T("< "));
pbi->size += len;
/*枚举节点的属性集合并格式化*/
if(EnumHashEntity(GetTreeDataItemHashTable(ptrNode),(EnumHashEntityPtr)XML_FormatNodeProper,(void*)pbi))
return -1;
len = GetTreeDataItemProperLength(ptrNode,NODETEXT);
if(pbi->size + len + (int)_tcslen(_T(">")) > pbi->max)
return -1;
/*格式化节点的文本节*/
if(pbi->pBuf)
{
szText = (TCHAR*)calloc(len + 1,sizeof(TCHAR));
ReadTreeDataItemProper(ptrNode,NODETEXT,szText,len);
_stprintf(pbi->pBuf + pbi->size,_T(">%s"),szText);
free(szText);
}
len = len + _tcslen(_T(">"));
pbi->size += len;
/*格式花子节点*/
if(XML_FormatNode(GetTreeDataChildItem(ptrNode),pbi) == -1)
return -1;
len = GetTreeDataItemProperLength(ptrNode,NODENAME) + _tcslen(_T("</>\r\n"));
if(pbi->size + len > pbi->max)
return -1;
/*格式化节点的尾部*/
if(pbi->pBuf)
{
_stprintf(pbi->pBuf + pbi->size,_T("</%s>\r\n"),szName);
free(szName);
}
pbi->size += len;
ptrNode = GetTreeDataNextSiblingItem(ptrNode);
}
return pbi->size;
}
/************************************************************
function: 格式化DOM树
ptrDom: DOM连接件指针
pBuf: 数据缓冲区
max: 缓冲区大小
return: 实际格式化的字节数,-1则出错
*************************************************************/
int XML_Format(LINKPTR ptrDom,TCHAR* pBuf,int max)
{
ProperBuf bi;
LINKPTR ptrNode = GetTreeDataRootItem(ptrDom);
bi.max = max;
bi.pBuf = pBuf;
bi.size = 0;
return XML_FormatNode(ptrNode,&bi);
}
/************************************************************
function: 取得格式化DOM的数据缓冲区的大小
ptrDom: DOM连接件指针
reutrn: 所需的缓冲区大小,-1则出错
*************************************************************/
int XML_FormatSize(LINKPTR ptrDom)
{
return XML_Format(ptrDom,NULL,MAX_INT);
}
/************************************************************
function: 将DOM写入XML文件
ptrDom: DOM连接件指针
szFile: 文件名称
xmlencode: 编码类型
return: 零值成功,非零值失败
*************************************************************/
int XML_WriteFile(LINKPTR ptrDom,const TCHAR* szFile,const TCHAR* xmlencode)
{
int len,tag;
TCHAR* pBuf;
FILE* file ;
TCHAR szXML[100];
file = _tfopen(szFile,_T("wt"));
if(file == NULL)
return DOM_FAIL;
if(xmlencode == NULL)
_stprintf(szXML,_T("<?xml version=%c1.0%c encoding=%cutf-8%c ?>\r\n"),_T('"'),_T('"'),_T('"'),_T('"'));
else
_stprintf(szXML,_T("<?xml version=%c1.0%c encoding=%c%s%c ?>\r\n"),_T('"'),_T('"'),_T('"'),xmlencode,_T('"'));
len = _tcslen(szXML);
if(len != (int)fwrite(szXML,sizeof(TCHAR),len,file))
{
fclose(file);
return DOM_FAIL;
}
len = XML_FormatSize(ptrDom);
if(len < 0)
{
fclose(file);
return DOM_FAIL;
}
pBuf = (TCHAR*)calloc(len + 1,sizeof(TCHAR));
tag = XML_Format(ptrDom,pBuf,len);
if(tag < 0 )
{
free(pBuf);
fclose(file);
return DOM_FAIL;
}
if(len != (int)fwrite(pBuf,sizeof(TCHAR),len,file))
{
free(pBuf);
fclose(file);
return DOM_FAIL;
}else
{
free(pBuf);
fclose(file);
return DOM_SUCCEED;
}
}
/************************************************************
function: 从XML文件中生成DOM树
ptrDom: DOM连接件指针
szFile: 文件名称
return: 零值成功,非零值失败
*************************************************************/
int XML_ReadFile(LINKPTR ptrDom,const TCHAR* szFile)
{
int len = 0;
void* pBuf;
FILE* file;
file = _tfopen(szFile,_T("r"));
if(file == NULL)
return DOM_FAIL;
while(_fgettc(file) != EOF)
len ++;
fseek(file,0,SEEK_SET);
pBuf = calloc(len + 1,sizeof(TCHAR));
if(len != (int)fread(pBuf,sizeof(TCHAR),len,file))
{
fclose(file);
free(pBuf);
return DOM_FAIL;
}
fclose(file);
XML_DeleteAllNodes(ptrDom);
if(XP_SUCCESS != XML_Parse(ptrDom,(TCHAR*)pBuf))
{
free(pBuf);
return DOM_FAIL;
}else
{
free(pBuf);
return DOM_SUCCEED;
}
}
有关连接件、二叉树请参见《编程手记之ANSI C篇-(一)通用连接件》,《编程手记之ANSI C篇-(三)二叉分析树》,谨以这些天来的一些叽叽歪歪之陈述,慕求志同道合之战友,与君共勉!