XML文件
xml为可扩展标记语言,是互联网数据传输的重要工具,它可以跨越互联网任何的平台,不受编程语言和操作系统的限制。同时,为了实现软件运行参数的可配置化,xml文件也是及其有效的手段。
XML文件格式
xml声明
<?xml version='1.0' encoding='gb2312'?>
xml声明一般是xml文档的第一行,xml声明由 version 和 encoding 组成,version 为文档规范版本,encoding 为文档字符编码,默认编码为 UTF-8。
xml元素
<test>789</test>
test为元素的标签,开始和结束的标签必须一一对应。
xml属性
<parameter type="runinfo" id="1">
</parameter>
属性值用双引号包裹;一个元素可以有多个属性,它的基本格式为:
<元素名 属性名=“属性值” 属性名=“属性值”>
xml实体
在xml中,一些字符有特殊的含义,例如 <,如果我们把 < 放到元素中,整个xml的格式就会出错:
<test>1 < 2</test>
为了避免出错,可以用实体引用来代替 < :
<test>1 < 2</test>
xml中5个预定义实体:
实体 | 符号 |
---|---|
< | < |
> | > |
& | & |
" | " |
' | ' |
XML示例
<?xml version='1.0' encoding='gb2312'?>
<scene name="parameter">
<parameter type="person">
<name>Tim</name>
<age>26</age>
</parameter>
</scene>
XML解析
使用 QtXml 模块对xml进行解析。
QFile *m_localfile;
QDomDocument *m_dom;
QTextStream *m_floStream;
QString m_fileName; // xml文件名
加载xml文件:
void XmlUtil::loadXml()
{
if (m_fileName != "")
{
m_localfile = new QFile(m_fileName);
if (!m_localfile->open(QFile::ReadWrite | QFile::Text)) {
qDebug() << "fail to open config1";
return;
}
m_floStream = new QTextStream(localfile);
QTextCodec *codec = QTextCodec::codecForName("GB2312");
m_floStream->setCodec(codec);
QString xmlDataStr = m_floStream->readAll();
m_dom = new QDomDocument();
if (!m_dom->setContent(xmlDataStr)) {
qDebug() << "fail to open config2";
m_localfile->close();
return;
}
}
}
释放xml文件:
void XmlUtil::freeXml()
{
if (localfile)
{
localfile->close();
delete localfile;
localfile = NULL;
}
if (dom)
{
delete dom;
dom = NULL;
}
if (floStream)
{
delete floStream;
floStream = NULL;
}
}
创建元素
// @param tagName:tag名,如 示例中的 parameter
// @param attriName:属性名,如 示例中的 type
// @param attriValue:属性值,如 示例中的 person
// @param keyNodeInfos:元素信息,如 {"name",["Tim"]},{"age",["26"]}
QDomNode XmlUtil::createNode(const QString &tagName, const QString &attriName, const QString &attriValue,
const QMap<QString, QStringList> &keyNodeInfos)
{
QDomElement mainElement = dom->createElement(tagName);
if (!attriName.isEmpty())
{
QDomAttr mainDomAttr = dom->createAttribute(attriName);
mainDomAttr.setValue(attriValue);
mainElement.setAttributeNode(mainDomAttr);
}
for (auto it = keyNodeInfos.constBegin(); it != keyNodeInfos.constEnd(); it++)
{
for (auto subIt = it.value().constBegin(); subIt != it.value().constEnd(); subIt++)
{
QDomElement subElement = dom->createElement(it.key());
QDomText nodeText = dom->createTextNode(*subIt);
subElement.appendChild(nodeText);
mainElement.appendChild(subElement);
}
}
return mainElement;
}
修改xml文件
/**
* 修改xml配置文件
* @param name: "scene"元素的"name"属性值
* @param type: "parameter"元素的"type"属性值
* @param key: 元素名,如"age"
* @param value:元素值,如果有多个,则创建多个元素
*/
bool XmlUtil::writeValue(const QString &name,const QString &type,const QString &key,const QStringList& value)
{
if (dom == NULL)
return false;
bool isExist = false;
QDomNodeList mainNodes = dom->childNodes();
// 先找到 "scene"元素的"name"属性值为 name 的子元素A
for (int mainNodeNum = 0; mainNodeNum < mainNodes.size(); mainNodeNum++)
{
QDomNode mainNode = mainNodes.at(mainNodeNum);
if (mainNode.toElement().attributeNode("name").value() == name)
{
QDomNode oldMainNode = mainNode;
// 再从子元素A中找到 "parameter"元素的"type"属性值为 type 的子元素B
QDomNodeList subNodes = mainNode.childNodes();
for (int subNodeNum = 0; subNodeNum < subNodes.size(); subNodeNum++)
{
QDomNode node = subNodes.at(subNodeNum);
if (node.toElement().attributeNode("type").value() == type)
{
QDomNode cloneNode = node;
// 再从子元素B中找到 元素名为key的元素
isExist = true;
QDomNodeList childNodes = node.childNodes();
int num = 0;
for (int childNodeNum = 0; childNodeNum < childNodes.count(); childNodeNum++)
{
QDomNode mynode = childNodes.at(childNodeNum);
if (mynode.nodeName() == key)
{
QDomNode myOldNode = mynode;
if (num < value.size())
{
// 设置元素值
if (mynode.hasChildNodes())
{
QDomNode oldNode = mynode.firstChild();
mynode.firstChild().setNodeValue(value.at(num));
QDomNode newNode = mynode.firstChild();
mynode.replaceChild(newNode, oldNode);
}
else
{
QDomText nodeText = dom->createTextNode(value.at(num));
mynode.appendChild(nodeText);
}
}
num++;
node.replaceChild(mynode, myOldNode);
}
}
// 如果现有元素个数num 小于 value的长度size,那么创建 size-num 个元素
for (; num < value.size(); num++)
{
QDomElement element = dom->createElement(key);
node.appendChild(element);
QDomText nodeText = dom->createTextNode(value.at(num));
element.appendChild(nodeText);
}
mainNode.replaceChild(node, cloneNode);
}
}
// 如果没有找到 "parameter"元素的"type"属性值为 type 的子元素B,那么创建它
if (!isExist)
{
QMap<QString, QStringList> keyNodeInfos;
keyNodeInfos.insert(key, value);
QDomNode subElement = createNode("parameter", "type", type, keyNodeInfos);
mainNode.appendChild(subElement);
isExist = true;
}
dom->replaceChild(mainNode,oldMainNode);
}
}
// 如果没有找到 "scene"元素的"name"属性值为 name 的子元素A,那么创建它
if (!isExist)
{
QMap<QString, QStringList> keyNodeInfos;
keyNodeInfos.insert(key, value);
QDomNode mainNode = createNode("scene", "name", name);
QDomNode subNode = createNode("parameter", "type", type, keyNodeInfos);
mainNode.appendChild(subNode);
dom->appendChild(mainNode);
isExist = true;
}
localfile->resize(0);
dom->save(*floStream,4);
return true;
}
读取xml文件
/**
* 获取配置文件中的值
* @param name: "scene"元素的"name"属性值
* @param type: "parameter"元素的"type"属性值
* @param key: 元素名,如"age"
* @return 元素值,如果找到多个满足要求的元素,那么返回链表
*/
QStringList XmlUtil::getValue(const QString &name,const QString &type,const QString &key)
{
QStringList result;
if (dom == NULL)
return result;
QDomNodeList nodelist = dom->elementsByTagName(name);
for(int i=0; i<nodelist.count(); i++)
{
QDomNode node = nodelist.at(i);
if (node.toElement().attributeNode("type").value() == type)
{
QDomNodeList itemlist = node.childNodes();
for(int j=0; j<itemlist.count(); j++)
{
QDomNode mynode = itemlist.at(j);
if (mynode.nodeName() == key)
result << mynode.firstChild().nodeValue();
}
}
}
if (result.isEmpty())
qDebug() << QString("there is no key as %1").arg(key);
return result;
}
这样的话,我们就可以很方便的读取、修改xml文件。例如:
- 读取示例xml中的age
int ageValue = getValue("parameter", "person", "age").first().toInt();
- 修改示例xml中的name
bool isSuccess = writeValue("parameter", "person", "name", QStringList() << "Peter");