第86集:XML文件操作

学习目标

  • 理解XML数据格式的基本概念
  • 掌握xml.etree.ElementTree模块的使用方法
  • 学会读取和解析XML文件
  • 掌握XML数据的创建和修改
  • 学会处理XML文件中的常见问题

一、XML概述

1.1 什么是XML

XML(eXtensible Markup Language,可扩展标记语言)是一种用于存储和传输数据的标记语言。它是一种自描述的格式,可以定义自己的标签。

1.2 XML的特点

  • 可扩展性:可以自定义标签
  • 结构化:使用树形结构组织数据
  • 跨平台:独立于操作系统和编程语言
  • 可读性:人类可读,易于理解和调试
  • 标准化:W3C标准,广泛应用

1.3 XML与JSON的对比

特性 XML JSON
可读性 较好 更好
数据量 较大 较小
解析速度 较慢 较快
注释支持 支持 不支持
属性支持 支持 不支持
命名空间 支持 不支持

1.4 XML基本结构

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <student id="1">
        <name>张三</name>
        <age>25</age>
        <city>北京</city>
    </student>
    <student id="2">
        <name>李四</name>
        <age>30</age>
        <city>上海</city>
    </student>
</root>

二、XML基本概念

2.1 元素(Element)

元素是XML的基本构建块,由开始标签、内容和结束标签组成。

<name>张三</name>

2.2 属性(Attribute)

属性提供元素的额外信息,位于开始标签中。

<student id="1">

2.3 文本内容(Text Content)

元素之间的文本内容。

<name>张三</name>

2.4 根元素(Root Element)

XML文档的顶层元素,只能有一个。

<root>
    <!-- 其他元素 -->
</root>

2.5 嵌套元素(Nested Elements)

元素可以包含其他元素。

<student>
    <name>张三</name>
    <age>25</age>
</student>

三、xml.etree.ElementTree模块

3.1 导入模块

import xml.etree.ElementTree as ET

3.2 主要类和函数

  • Element:表示XML元素
  • ElementTree:表示整个XML文档树
  • **parse()**:解析XML文件
  • **fromstring()**:从字符串解析XML
  • **tostring()**:将元素转换为字符串

四、解析XML文件

4.1 使用parse()解析XML文件

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()

4.2 使用fromstring()解析XML字符串

import xml.etree.ElementTree as ET

xml_str = '<root><name>张三</name></root>'
root = ET.fromstring(xml_str)

4.3 遍历XML元素

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()

# 遍历所有子元素
for child in root:
    print(child.tag, child.attrib)

五、访问XML数据

5.1 访问元素标签

import xml.etree.ElementTree as ET

root = ET.fromstring('<root><name>张三</name></root>')
print(root.tag)  # 输出:root

5.2 访问元素属性

import xml.etree.ElementTree as ET

root = ET.fromstring('<root id="1"><name>张三</name></root>')
print(root.attrib)  # 输出:{'id': '1'}
print(root.get('id'))  # 输出:1

5.3 访问元素文本内容

import xml.etree.ElementTree as ET

root = ET.fromstring('<root><name>张三</name></root>')
name = root.find('name')
print(name.text)  # 输出:张三

5.4 访问子元素

import xml.etree.ElementTree as ET

root = ET.fromstring('<root><name>张三</name><age>25</age></root>')

# 使用find()查找第一个匹配的子元素
name = root.find('name')
print(name.text)

# 使用findall()查找所有匹配的子元素
children = root.findall('*')
for child in children:
    print(child.tag, child.text)

六、创建XML文件

6.1 创建元素

import xml.etree.ElementTree as ET

root = ET.Element('root')
name = ET.SubElement(root, 'name')
name.text = '张三'

6.2 添加属性

import xml.etree.ElementTree as ET

root = ET.Element('root')
root.set('id', '1')

6.3 添加子元素

import xml.etree.ElementTree as ET

root = ET.Element('root')
student = ET.SubElement(root, 'student')
name = ET.SubElement(student, 'name')
name.text = '张三'

6.4 保存XML文件

import xml.etree.ElementTree as ET

root = ET.Element('root')
name = ET.SubElement(root, 'name')
name.text = '张三'

tree = ET.ElementTree(root)
tree.write('output.xml', encoding='utf-8', xml_declaration=True)

七、修改XML文件

7.1 修改元素文本

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()

name = root.find('name')
name.text = '李四'

tree.write('data.xml', encoding='utf-8')

7.2 修改元素属性

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()

root.set('id', '2')
tree.write('data.xml', encoding='utf-8')

7.3 添加新元素

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()

age = ET.SubElement(root, 'age')
age.text = '25'

tree.write('data.xml', encoding='utf-8')

7.4 删除元素

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()

name = root.find('name')
root.remove(name)

tree.write('data.xml', encoding='utf-8')

八、XML数据查询

8.1 使用find()查询

import xml.etree.ElementTree as ET

root = ET.fromstring('''
<root>
    <student id="1">
        <name>张三</name>
    </student>
    <student id="2">
        <name>李四</name>
    </student>
</root>
''')

# 查找第一个student元素
student = root.find('student')
print(student.get('id'))

8.2 使用findall()查询

import xml.etree.ElementTree as ET

root = ET.fromstring('''
<root>
    <student id="1">
        <name>张三</name>
    </student>
    <student id="2">
        <name>李四</name>
    </student>
</root>
''')

# 查找所有student元素
students = root.findall('student')
for student in students:
    print(student.get('id'))

8.3 使用XPath查询

import xml.etree.ElementTree as ET

root = ET.fromstring('''
<root>
    <student id="1">
        <name>张三</name>
        <age>25</age>
    </student>
    <student id="2">
        <name>李四</name>
        <age>30</age>
    </student>
</root>
''')

# 查询所有name元素
names = root.findall('.//name')
for name in names:
    print(name.text)

# 查询id为1的student
student = root.find(".//student[@id='1']")
print(student.find('name').text)

九、XML数据处理

9.1 遍历XML树

import xml.etree.ElementTree as ET

def traverse_xml(element, level=0):
    indent = '  ' * level
    print(f"{indent}{element.tag}: {element.text}")
    for child in element:
        traverse_xml(child, level + 1)

root = ET.fromstring('<root><name>张三</name><age>25</age></root>')
traverse_xml(root)

9.2 XML转换为字典

import xml.etree.ElementTree as ET

def xml_to_dict(element):
    result = {}
    for child in element:
        if len(child) > 0:
            result[child.tag] = xml_to_dict(child)
        else:
            result[child.tag] = child.text
    return result

root = ET.fromstring('<root><name>张三</name><age>25</age></root>')
data = xml_to_dict(root)
print(data)

9.3 字典转换为XML

import xml.etree.ElementTree as ET

def dict_to_xml(data, root_tag='root'):
    root = ET.Element(root_tag)
    for key, value in data.items():
        child = ET.SubElement(root, key)
        if isinstance(value, dict):
            child.extend(dict_to_xml(value, key))
        else:
            child.text = str(value)
    return root

data = {'name': '张三', 'age': '25'}
root = dict_to_xml(data)
print(ET.tostring(root, encoding='unicode'))

十、XML文件常见问题

10.1 编码问题

import xml.etree.ElementTree as ET

# 写入XML文件时指定编码
root = ET.Element('root')
tree = ET.ElementTree(root)
tree.write('data.xml', encoding='utf-8', xml_declaration=True)

# 读取XML文件时指定编码
tree = ET.parse('data.xml')
root = tree.getroot()

10.2 处理命名空间

import xml.etree.ElementTree as ET

xml_str = '''
<root xmlns="http://example.com">
    <name>张三</name>
</root>
'''

root = ET.fromstring(xml_str)
namespace = {'ns': 'http://example.com'}
name = root.find('ns:name', namespace)
print(name.text)

10.3 处理CDATA

import xml.etree.ElementTree as ET

# ElementTree不直接支持CDATA,需要使用其他方法
# 可以使用xml.dom.minidom模块处理CDATA
from xml.dom import minidom

doc = minidom.Document()
root = doc.createElement('root')
doc.appendChild(root)

cdata = doc.createCDATASection('<special>data</special>')
name = doc.createElement('name')
name.appendChild(cdata)
root.appendChild(name)

print(doc.toxml())

10.4 处理XML注释

import xml.etree.ElementTree as ET

# ElementTree不直接支持注释
# 可以使用xml.dom.minidom模块处理注释
from xml.dom import minidom

doc = minidom.Document()
root = doc.createElement('root')
doc.appendChild(root)

comment = doc.createComment('这是一个注释')
root.appendChild(comment)

name = doc.createElement('name')
name.appendChild(doc.createTextNode('张三'))
root.appendChild(name)

print(doc.toxml())

十一、XML高级操作

11.1 美化XML输出

import xml.etree.ElementTree as ET
from xml.dom import minidom

def prettify(element):
    rough_string = ET.tostring(element, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")

root = ET.Element('root')
name = ET.SubElement(root, 'name')
name.text = '张三'

print(prettify(root))

11.2 XML验证

import xml.etree.ElementTree as ET

def validate_xml(xml_str):
    try:
        ET.fromstring(xml_str)
        return True, "XML格式正确"
    except ET.ParseError as e:
        return False, f"XML格式错误:{e}"

# 有效的XML
valid_xml = '<root><name>张三</name></root>'
is_valid, message = validate_xml(valid_xml)
print(f"是否有效:{is_valid}, 消息:{message}")

# 无效的XML
invalid_xml = '<root><name>张三</name>'
is_valid, message = validate_xml(invalid_xml)
print(f"是否有效:{is_valid}, 消息:{message}")

11.3 合并XML文件

import xml.etree.ElementTree as ET

def merge_xml(file1, file2, output):
    tree1 = ET.parse(file1)
    tree2 = ET.parse(file2)
    
    root1 = tree1.getroot()
    root2 = tree2.getroot()
    
    for child in root2:
        root1.append(child)
    
    tree1.write(output, encoding='utf-8', xml_declaration=True)

11.4 XML数据转换

import xml.etree.ElementTree as ET

def transform_xml(input_file, output_file, transform_func):
    tree = ET.parse(input_file)
    root = tree.getroot()
    
    transform_func(root)
    
    tree.write(output_file, encoding='utf-8', xml_declaration=True)

# 使用示例
def add_id_attribute(root):
    for i, child in enumerate(root, 1):
        child.set('id', str(i))

十二、最佳实践

12.1 使用with语句

虽然ElementTree不支持with语句,但可以手动确保文件关闭。

import xml.etree.ElementTree as ET

tree = ET.parse('data.xml')
root = tree.getroot()
# 处理数据
tree.write('output.xml', encoding='utf-8')

12.2 指定编码方式

# 推荐
tree.write('data.xml', encoding='utf-8', xml_declaration=True)

# 不推荐
tree.write('data.xml')

12.3 异常处理

import xml.etree.ElementTree as ET

try:
    tree = ET.parse('data.xml')
    root = tree.getroot()
except FileNotFoundError:
    print('文件不存在')
except ET.ParseError:
    print('XML格式错误')
except Exception as e:
    print(f'发生错误:{e}')

12.4 使用XPath查询

import xml.etree.ElementTree as ET

root = ET.fromstring(xml_str)

# 推荐:使用XPath
students = root.findall('.//student')

# 不推荐:手动遍历
for child in root:
    if child.tag == 'student':
        process(child)

十三、总结

本集学习了XML文件操作的知识:

  • XML数据格式的基本概念和结构
  • xml.etree.ElementTree模块的使用方法
  • XML文件的读取和解析
  • XML数据的创建和修改
  • XML数据的查询和处理
  • XML文件常见问题和最佳实践

XML是一种重要的数据交换格式,在配置文件、Web服务、数据存储等场景中广泛应用。下一集我们将学习Excel文件操作。

十四、练习题

  1. 创建一个XML文件,包含学生信息(姓名、年龄、成绩、城市),然后读取并显示。
  2. 编写一个函数,将XML元素转换为字典。
  3. 实现一个函数,查询特定属性值的元素。
  4. 编写一个程序,合并两个XML文件的数据。
  5. 实现一个函数,美化XML输出。
  6. 编写一个函数,验证XML文件的有效性。
  7. 实现一个程序,统计XML文件中特定元素的数量。
  8. 编写一个程序,将JSON文件转换为XML文件。
« 上一篇 JSON文件操作 下一篇 » 文件路径操作