XML 压缩工具
通过去除空白和注释来压缩 XML
XML 输入
压缩后的 XML
什么是 XML 压缩?
XML 压缩是指在不改变文档含义的前提下,去除 XML 文档中所有多余字符的过程。XML 压缩工具会删除标签之间的空白、去除注释、消除换行符并折叠缩进,从而生成紧凑的单行输出。压缩后的 XML 字符串与原始格式化版本被解析器等价读取,生成完全相同的数据模型。
XML 1.0 规范(W3C 推荐标准,第五版)在第 2.10 节中定义了空白处理规则。标签之间没有语义价值的空白称为「无意义空白」,XML 处理器允许将其丢弃。然而,文本内容中的空白默认是有意义的,除非父元素声明了 xml:space="default"。正确的 XML 压缩工具会区分这两种情况,只删除可以安全去除的部分。
压缩与压缩算法不同。Gzip 或 Brotli 在传输层减小体积,解析前需要解压缩。而压缩则直接减小原始文档体积,使 XML 无需解压步骤即可被任何解析器读取。实践中,先压缩再经压缩算法处理可获得最佳效果:先消除冗余字符,再让压缩算法处理更紧凑的输入。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Product catalog for Q1 2026 -->
<catalog>
<product id="p101">
<name>Widget A</name>
<price currency="USD">29.99</price>
<!-- Temporarily discounted -->
<stock>142</stock>
</product>
<product id="p102">
<name>Widget B</name>
<price currency="EUR">19.50</price>
<stock>87</stock>
</product>
</catalog><?xml version="1.0" encoding="UTF-8"?><catalog><product id="p101"><name>Widget A</name><price currency="USD">29.99</price><stock>142</stock></product><product id="p102"><name>Widget B</name><price currency="EUR">19.50</price><stock>87</stock></product></catalog>
为什么使用 XML 压缩工具?
带缩进和注释的格式化 XML 非常适合开发和代码审查。但用于存储、传输和机器消费时,多余的格式只会增加字节数而没有任何好处。XML 压缩工具正好填补了这一差距。
XML 压缩工具使用场景
XML 压缩可去除的内容
并非 XML 文档中的所有内容都可以安全去除。下表列出了每种可移除内容的类型,以及丢弃它是否始终安全或取决于具体使用场景。
| 内容类型 | 示例 | 安全性 |
|---|---|---|
| Indentation | Spaces/tabs before tags | Always safe to remove |
| Line breaks | \n and \r\n between tags | Always safe to remove |
| Comments | <!-- ... --> | Safe unless parsed by app |
| XML declaration | <?xml version="1.0"?> | Keep if encoding is non-UTF-8 |
| Processing instructions | <?xml-stylesheet ...?> | Keep if consumed downstream |
| Trailing whitespace | Spaces after closing tags | Always safe to remove |
| Text node whitespace | Spaces inside text content | Remove only between tags, not within |
压缩 vs Gzip vs 二进制格式
压缩、压缩算法和二进制编码各自针对体积问题的不同层面。压缩将输出保持为有效、人类可读的 XML。压缩算法(Gzip、Brotli)可进一步缩小体积,但解析前需要解压步骤。二进制格式压缩效果最强,但连接两端都需要兼容的编码/解码器——主要用于嵌入式系统或大量使用 WSDL 的企业级服务。
代码示例
以编程方式压缩 XML 在各语言中遵循相同模式:将文档解析为树形结构,可选地删除注释节点,然后不带缩进地序列化输出。
// Minify XML by parsing and re-serializing (strips formatting)
const raw = `<root>
<item id="1">
<!-- note -->
<name>Test</name>
</item>
</root>`
const parser = new DOMParser()
const doc = parser.parseFromString(raw, 'application/xml')
// Remove comment nodes
const walker = doc.createTreeWalker(doc, NodeFilter.SHOW_COMMENT)
const comments = []
while (walker.nextNode()) comments.push(walker.currentNode)
comments.forEach(c => c.parentNode.removeChild(c))
const minified = new XMLSerializer().serializeToString(doc)
// → "<root><item id=\"1\"><name>Test</name></item></root>"from lxml import etree
xml = """<root>
<item id="1">
<!-- note -->
<name>Test</name>
</item>
</root>"""
tree = etree.fromstring(xml.encode())
# Remove comments
for comment in tree.iter(etree.Comment):
comment.getparent().remove(comment)
# Serialize without pretty-print (minified)
result = etree.tostring(tree, xml_declaration=False).decode()
# → '<root><item id="1"><name>Test</name></item></root>'
# With xml.etree (stdlib, no lxml needed)
import xml.etree.ElementTree as ET
root = ET.fromstring(xml)
ET.indent(root, space='') # Python 3.9+
print(ET.tostring(root, encoding='unicode'))package main
import (
"encoding/xml"
"fmt"
"strings"
)
func minifyXML(input string) (string, error) {
decoder := xml.NewDecoder(strings.NewReader(input))
var out strings.Builder
encoder := xml.NewEncoder(&out)
// No indentation = minified output
for {
tok, err := decoder.Token()
if err != nil {
break
}
// Skip comments
if _, ok := tok.(xml.Comment); ok {
continue
}
// Skip whitespace-only char data
if cd, ok := tok.(xml.CharData); ok {
if strings.TrimSpace(string(cd)) == "" {
continue
}
}
encoder.EncodeToken(tok)
}
encoder.Flush()
return out.String(), nil
}
// minifyXML("<a>\n <b>1</b>\n</a>") → "<a><b>1</b></a>"# Minify XML with xmllint (part of libxml2) xmllint --noblanks input.xml > minified.xml # Minify from stdin echo '<root> <item>hello</item> </root>' | xmllint --noblanks - # → <?xml version="1.0"?><root><item>hello</item></root> # Strip comments too (combine with sed or xmlstarlet) xmlstarlet ed -d '//comment()' input.xml | xmllint --noblanks - # Check size reduction echo "Before: $(wc -c < input.xml) bytes" echo "After: $(xmllint --noblanks input.xml | wc -c) bytes"