读取XML文件 4
前言
上次的XML解析器还有很多地方需要完善,本篇文章就相当于对上篇文章进行补充,终于不是删除所有进行重写了。
多说一句, visual studio中使用foreach自动补全的代码没办法直接用?比如这样:
正文
1、XML文件有一种节点的写法笔者之前一直没太在意,就是这种直接将字符串写在节点里:
<author name ="JI">
<age>22</age>
</author>
可以改成:
<author name ="JI" age="22"/>
没办法多属性的情况,比如:
<question id="1" name="问东问西一星期,走南走北两公里,心上心下比Tiny,下一句是?">
<option>一天一地Orz。</option>
<option>看得心态要崩溃。</option>
<option>代码多出六百倍。</option>
<option>真的不会感觉累?</option>
</question>
可以改写成这种形式:
<option value="一天一地Orz"/>
所以没有为这种形式写解析,但在网上找了找发现有这种写法的文件真的很多,好,还是添上吧!
写在节点里的字符串应该被当做文本节点,但此节点只有内容,而且一个节点中只会出现一次文本,所以就将它简化成属性。
LLXMLNode类中添加方法wstring GetInnerText(){return innerText;};和属性wstring innerText;。
在LoadUnknown方法里添加到当首字符不为’<’的判断里:
else if(!nodeStack.empty())
{
wchar_t* textStart = fileBuffer;
while (*fileBuffer!=L'<')
{
fileBuffer++;
bufferSize--;
if (bufferSize == 0)
{
return false;
}
}
wstring innerText = wstring(textStart, fileBuffer - textStart);//此处没有添加截取空字符的操作,可以自行写截取方法。格式化字符串的方法在下面。
nodeStack.top()->SetInnerText(innerText);
}
XML应该允许下列情况(节点中既有文本又有子节点)吧!
<Game id="1">Text
<Node/>
</Game>
2、属性值可以使用单引号和双引号,上篇文章只做了双引号的判断,本文进行补充。
需要在判断*fileBuffer==L’”’后加*fileBuffer==L’ \’’即可,还要修改一下其他地方,需要判断单引号开始要单引号结束,前后对应才算格式正确。将在LoadProperty中判断是单引号还是双引号放在LoadFormatValue里进行比较,代码就不展示了。
3、进行转义字符格式化:
先定义wstringstream wsstream;
#include <sstream>
wstring LLXMLDocument::FormatWStringFromXML(wstring ws)
{
wsstream.str(L"");//需要进行两步操作,只用clear的话wsstream内的保存的字符串是不变的。
wsstream.clear();//需要清空缓存
int curPos = 0;
int wsSize = ws.size();
while (curPos<wsSize)
{
if (ws[curPos] == L'&')
{
if (ws.substr(curPos,6)==L""")
{
wsstream << L'"';
curPos += 6;
}
else if (ws.substr(curPos, 6) == L"'")
{
wsstream << L'\'';
curPos += 6;
}
else if (ws.substr(curPos, 5) == L"&")
{
wsstream << L'&';
curPos += 5;
}
else if (ws.substr(curPos, 4) == L"<")
{
wsstream << L'<';
curPos += 4;
}
else if (ws.substr(curPos, 4) == L">")
{
wsstream << L'>';
curPos += 4;
}
else
{
wsstream << ws[curPos];
curPos++;
}
}
else
{
wsstream << ws[curPos];
curPos++;
}
}
return wsstream.str();
}
输出与上同理逆向。
4、保存节点和属性:
void LLXMLDocument::SaveNode(wofstream& file, LLXMLNode* node, int depth)
{
for (int i=0;i<depth;i++)
{
file << L'\t';
}
file << L"<" + node->GetName() ;
if (node->GetPropertyMap().size()!=0)
{
file << L" ";
for (auto var : node->GetPropertyMap())
{
SaveProperty(file, var.second);
file << L" ";
}
}
bool tempBool = false;
if (node->GetInnerText() != L""|| node->GetChildNodeList().size() > 0)
{
tempBool = true;
}
if (!tempBool)
{
file << L"/>"<< endl;
return;
}
file <<L">"<< endl;
if (node->GetInnerText() != L"")
{
for (int i = 0; i < depth+1; i++)
{
file << L'\t';
}
file << node->GetInnerText() << endl;
}
for (auto var : node->GetChildNodeList())
{
SaveNode(file, var,depth+1);
}
for (int i = 0; i < depth; i++)
{
file << L'\t';
}
file << L"</" << node->GetName() << L">"<<endl;
}
void LLXMLDocument::SaveProperty(wofstream & file, LLXMLProperty * llProperty)
{
file << llProperty->GetName() << L"=" << L'"' << FormatWStringToXML(llProperty->GetValue()) << L'"';
}
真正保存时只要传入LLXMLDocument.SaveNode(file,rootNode,0)就可以了。
结束语
暂时补充这么多,如果各位发现了哪里有错误和不足,请指正。