c#通过linq方式加载xml_XML 文件解析

目录:

  1. XML 介绍
  2. ElementTree 基础讲解 2.1 节点属性 2.2 节点搜索
  3. 获取矩形框的坐标和类别
  4. 参考

我需要对目标检测标注工具 labelimg 得到的xml文件进行解析,得到其中矩形框对应的类别和坐标值,所以对python下面的ElementTree研究了一下。

1. XML 介绍

XML全称为可扩展标记语言(Extensible Markup Language),在很多应用程序中XML编码格式的数据被用来在系统之间构建、存储和传输数据。现在用来与服务端交互的主要是Json和XML格式的数据,常用于网页数据抓取和解析结构化文档。

本文使用python内置的ElementTree包来解析XML文件,从而提取出有用的数据。

ElementTree 将整个XML文件放到内存中,将XML数据在内存中解析成一个树结构,通过树来操作XML,可以任意遍历树的节点,树中的每个节点都是一个对象。

XML 是一种分级的树状数据形式,它被表示为一棵树,ET 有两个对象来实现这个目的:

  1. ElementTree 将整个 XML 文件解析为一棵树;
  2. Element 将单个结点及其子节点解析为树。

2. ElementTree 基础讲解

我用这个 XML 文件做例子进行讲解:

<data version="2.0">
    <items>
        <item name="item1"> item1abc </item>
        <item name="item2"> item2abc </item>
    </items>
    <object> human </object>
</data>

首先导入相关模块并加载xml文件:

import xml.etree.ElementTree as ET

# 加载xml文件
tree = ET.parse('10.xml')
# tree = ET.ElementTree(file='10.xml')# 

# 获得root Element节点
root = tree.getroot()

ET.parseET.ElementTree 都可以加载xml文件,好像没什么区别,前者用得比较多。

getroot() 得到xml树的根节点,一旦我们能够访问根节点,我们就可以很容易地遍历树中的所有子结点(Element),因为树是一个连通图。

2.1 节点属性

还可以获得节点的一些属性,一个节点通常为:

<tag attrib> text </tag> tail

比如下面这个例子:

<item name='zll'> hello123 </item>
  • tag:表示节点名字,即item
  • attrib:即属性,用字典的形式保存,即{'name': 'zll'}
  • text:文本字符串,可以用来存储一些数据,即hello123,当这之间是其子节点时,此处为空字符
  • tail:尾字符串,并不是必须的,例子中没有这个属性。

查看根节点的属性:

print("tag: {}, attrib: {}".format(root.tag, root.attrib))

# 输出:
# tag: data, attrib: {'version': '2.0'}

查看根节点下一级子节点(items和object):

print(len(root[0]))  # 连接根节点的子节点数
for elem in root:
    print(elem.tag, elem.attrib, elem.text)

# 输出:
# 2
# items {} 
# object {}  human

两个子节点的 attrib 都为空字典,且因为 items 下面还有子节点,所以 items.text 为空。

也可以通过索引选择子结点:

print(root[0][0].tag, root[0][0].attrib, root[0][0].text)

# 输出
# ('item', {'name': 'item1'}, 'item1abc')

可以通过递归获取 XML 中的所有节点,ElementTree 对象和 Element 对象有一个 iter 方法可以创建一个树迭代器,以对所有结点进行深度优先遍历。

# 遍历xml文件中的所有节点
for elem in tree.iter():
    print(elem.tag, elem.attrib)

# 输出:
# data {'version': '2.0'}
# items {}
# item {'name': 'item1'}
# item {'name': 'item2'}
# object {}

iter() 方法也可以接收一个tag,表示只遍历指定名字的节点:

for elem in tree.iter(tag='item'):
    print(elem, elem.tag, elem.text)

# 输出:
# <Element 'item' at 0x7f86d0179958> item item1abc
# <Element 'item' at 0x7f86d01798b8> item item2abc

2.2 节点搜索

当XML文件较大或者其中的子节点 tag 非常多的时候,一个一个获取比较麻烦而且有很多不是需要的,可以通过 find() 或者 findall() 方法来查找指定 tag 的节点。

  • find(match):获取树中第一个与tag或path匹配的子节点
  • findall(match):获取所有与tag或path匹配的子节点,并以列表的形式返回
  • findtext(match):获取树中第一个与tag或path匹配的子节点的文本
for elem in root:
    print(elem.find('item'))
    print(elem.findtext('item'))

# 输出:
# <Element 'item' at 0x7f86d0179d68>
# item1abc
# None
# None

上面是找到节点中第一个名为item的子节点和其文本内容,因为 object 节点没有名为 item 的子节点,所以返回 None。

下面程序为找到当前节点下名为item的所有子节点:

for elem in root:
    for subelem in elem.findall('item'):
        print(subelem.attrib)
        print(subelem.get('name'))

# 输出:
# {'name': 'item1'}
# item1
# {'name': 'item2'}
# item2

上面是只通过 tag(节点名)来查找节点,也可以通过节点的路径来找到所有节点:

for elem in root.findall('./items/item'):  # path
    print(elem.attrib)

# 输出:
# {'name': 'item1'}
# {'name': 'item2'}

3. 获取矩形框的坐标和类别

我用从 imglabel 工具得到的一个xml文件作为例子:

<annotation version="2.0">
    <folder>images</folder>
    <filename>00001.jpg</filename>
    <path>C:imglabelimages00001.jpg</path>
    <source>
        <database>Unknown</database>
    </source>
    <size>
        <width>33</width>
        <height>495</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>dog</name>
        <pose>Unspecified</pose>
        <truncated>1</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>139</xmin>
            <ymin>152</ymin>
            <xmax>221</xmax>
            <ymax>257</ymax>
        </bndbox>
    </object>
    <object>
        <name>cat</name>
        <pose>Unspecified</pose>
        <truncated>1</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>55</xmin>
            <ymin>46</ymin>
            <xmax>122</xmax>
            <ymax>113</ymax>
        </bndbox>
    </object>
</annotation>

这个文件主要有如下内容:

  1. (folder)文件夹名称
  2. (filename)图片名
  3. (path)文件路径
  4. (size)图片宽高及深度
  5. (object->name)目标的类别名称
  6. (object->bndbox)边框坐标

我们主要是想获得的是 object 节点下的 namebndbox,它们分别对应矩形框的目标类别和坐标值。

程序如下:

import xml.etree.ElementTree as ET

tree = ET.ElementTree(file="10.xml")
root = tree.getroot()

boxes = []
for elem in root.iter(tag='object'):
    # 获取矩形框坐标
    box = [int(el.text) for el in  elem.find('bndbox')]
    # 获取目标类别
    box.append(elem.find('name').text)

    boxes.append(box)

print(boxes)

# 输出:
# [[139, 152, 221, 257, 'dog'], [55, 46, 122, 113, 'cat']]

上面涉及到的知识点前面都有讲解,就不多说了,最后输出结果为一个列表,列表中的每个元素为一个矩形框的坐标值和类别。

4. 参考

xml.etree.ElementTree API

Reading and Writing XML Files in Python

Python XML with ElementTree: Beginner's Guide

Working with XML files in Python

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值