根本原因是XML编码不匹配或根节点名错误;Qt默认UTF-8解析,若文件为GBK/UTF-16且无BOM或encoding声明,setContent()静默失败导致documentElement()为空,firstChildElement()均返回空元素。

QDomDocument读取XML时解析失败,firstChildElement()返回空元素
根本原因通常是XML文本编码不匹配或根节点名称写错。Qt默认按UTF-8解析,如果文件是GBK/UTF-16且没声明BOM或encoding属性,setContent()会静默失败,但不报错——此时documentElement()为空,后续所有firstChildElement()都返回空QDomElement。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 先用
QFile::open()读取原始字节,用QTextCodec::codecForName("GBK")->toUnicode()(或QString::fromUtf8())手动解码再传给setContent() - 检查XML第一行是否含
<?xml version="1.0" encoding="GBK"?>,若实际是UTF-8却写成GBK,也会解析失败 - 调用
setContent()后立刻检查返回值:if (!doc.setContent(&fileContent, &errorMsg, &errorLine)) { qDebug()
用QDomElement遍历子节点时漏掉文本节点或注释
QDomElement的childNodes()包含所有类型节点:元素、文本、注释、CDATA等。直接用item(i).toElement()会跳过非元素节点,导致遍历时“消失”——比如两个<item>之间有换行,实际生成了QDomText节点,但toElement()返回空,索引却继续+1,结果跳过下一个有效元素。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 遍历前先过滤:
if (node.isElement() && node.toElement().tagName() == "item") { ... } - 不要依赖
childNodes().size()作为循环上限,改用迭代器:for (QDomNode node = parent.firstChild(); !node.isNull(); node = node.nextSibling()) - 调试时打印节点类型:
qDebug() (<code>QDomNode::ElementNode是1,TextNode是3)
写入XML时中文乱码或格式错乱
QDomDocument::save()默认用UTF-8编码写入,但如果目标文件被其他程序(如Windows记事本)以ANSI打开,就会显示乱码;另外,save()不自动缩进,直接输出为一行,不利于人工查看和diff。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 显式指定编码和缩进:
doc.save(file, 2, QDomNode::EncodingFromDocument);——第二个参数是缩进空格数,第三个参数确保XML声明里写encoding="UTF-8" - 若必须输出GBK,先用
QTextStream写入:QTextStream out(&file); out.setCodec("GBK"); out ,但注意这会丢失XML声明中的encoding属性 - 避免在
QDomText中直接塞含<、&的字符串,应调用createTextNode()让Qt自动转义
QDomDocument在多线程中读写同一个XML文件
QDomDocument本身不是线程安全的,但更关键的是:多个线程同时用QFile读写同一物理文件,会导致内容覆盖或读到截断数据。Qt不提供文件级锁,QDomDocument也不感知底层文件状态。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 读操作可共用一个
QDomDocument实例(只读),但写操作必须串行化——用QMutex保护整个“读取→修改→保存”流程 - 更稳妥的做法是每次写入前用
QFile::rename()把原文件备份,写入新文件后再原子替换,避免写到一半崩溃导致数据丢失 - 不要在子线程里直接调用
QDomDocument::setContent()读取网络响应体,除非确认该响应已是完整UTF-8字节流;HTTP响应头里的charset可能与实际内容不符










