在XML文档的处理过程中,同一个元素节点上如果定义了名称相同的属性,就属于重复属性。虽然严格的XML规范不允许这种情况,但在数据生成或拼接环节常常会出现。我们需要通过解析和重写来删除重复属性,通常保留第一次出现的属性值。

为什么会出现重复属性
重复属性多源于多个系统字段合并、模板重复渲染或手工编辑失误。例如下面这段XML就不合法:
<user id="1" id="2" name="tom"></user>
实际解析时部分库会直接报错,部分会忽略后续同名属性。我们应在生成后或读取时主动清理。
使用Python删除重复属性
Python标准库xml.etree.ElementTree会把重复属性静默去重,但我们可以显式控制逻辑。以下示例演示如何遍历并重建属性字典:
import xml.etree.ElementTree as ET
def clean_duplicates(elem):
# 利用字典保留首次出现的属性
seen = {}
for k, v in elem.attrib.items():
if k not in seen:
seen[k] = v
elem.attrib.clear()
elem.attrib.update(seen)
for child in elem:
clean_duplicates(child)
tree = ET.parse('data.xml')
root = tree.getroot()
clean_duplicates(root)
tree.write('clean.xml', encoding='utf-8')
使用XSLT转换剔除重复属性
XSLT可在转换时只输出首次出现的属性。核心思路是用一个变量记录已处理属性名:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<xsl:copy>
<xsl:for-each select="@*">
<xsl:if test="not(@*[name()=name(current()) and . != current()])">
<xsl:copy/>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Java DOM方式处理
在Java里可先读取属性名集合,再调用removeAttribute移除后重新添加:
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
public class CleanXml {
static void clean(Node node) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element el = (Element) node;
NamedNodeMap map = el.getAttributes();
// 记录首次属性
java.util.LinkedHashMap<String, String> attrs = new java.util.LinkedHashMap<>();
for (int i = 0; i < map.getLength(); i++) {
Attr a = (Attr) map.item(i);
attrs.putIfAbsent(a.getName(), a.getValue());
}
for (int i = map.getLength() - 1; i >= 0; i--) {
el.removeAttribute(map.item(i).getName());
}
attrs.forEach(el::setAttribute);
}
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
clean(children.item(i));
}
}
public static void main(String[] args) throws Exception {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(new File("data.xml"));
clean(doc.getDocumentElement());
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.transform(new DOMSource(doc), new StreamResult(new File("clean.xml")));
}
}
操作建议
- 在数据源生成阶段增加校验,避免产生重复属性。
- 清洗时统一保留首次出现值,保证业务含义稳定。
- 对大型文件优先使用流式或XSLT方式以提升效率。
通过上述方法,你可以根据实际技术栈选择合适的XML删除重复属性的操作方法,保障文档合规与解析稳定。