为解决不同设备间的信息交换,可以通过ros、zmq等消息系统机制,也可以通过结构化文件进行存储,两种方式适用不同的情况。这里总结一下python的结构化文件存储方面的内容,主要包括xml和json。
一、xml
xml的一些基础内容:
XML文档的构成
- 处理指令(可以认为一个文件内只有一个处理指令)
- 最多只有一行
- 且必须在第一行
- 内容是与xml本身处理起相关的一些声明或者指令
- 以xml关键字开头
- 一般用于声明XML的版本和采用的编码
- version属性是必须的
- encoding属性用来支出xml解释器使用的编码
- 根元素(一个文件内只有一个根元素)
- 在整个xml文件中,可以把他看作一个树形结构
- 根元素有且只能由一个
- 子元素
- 属性
- 内容
- 表明标签所存储的信息
- 注释
- 起说明作用的信息
- 注释不能嵌套在标签里
- 只有在注释的开始和结尾使用双短横线
- 三短横线只能出现在注释的开头而不能用在结尾
<name> <!-- wangdapeng --> </name> #可以
<name <!-- wangdapeng -->> </name> #不可以,注释在标签内
<!--my-name-by-wang--> #可以,注释内容可以有一个短横线
<!--my--name--by--wang-->#不可以,双短横线只能出现在开头或结尾
<!---my-name--> #可以, 三短横线只能出现在开头
<!---my-name---> #不可以, 三短横线只能出现在开头
- 保留字符的处理
- XML中使用的符号可能跟实际符号相冲突,典型的就是左右尖括号
- 使用实体引用(EntityReference)来表示保留字符
<score> score>80 </score> #有错误,xml中不能出现>
<score> score>80</score> #使用实体引用
- 把含有保留字符的部分放在CDATA块内部,CDATA块把内部信息视为不需要转义
<![CDATA[
select name,age
from Student
where score>80
]]>
- 常用的需要转移的保留字符和对应实体引用
- &:&
- <:<
- >:>
- ':'
- ":"
- 一共五个, 每个实体引用都以&开头并且以分号结尾
- XML标签的命名规则
- Pascal命名法
- 用单词表示,第一个字母大写
- 大小写严格区分
- 配对的标签必须一致
- 命名空间
- 为了防止命名冲突
<Student>
<Name>LiuYing</Name>
<Age>23</Age>
</Student>
<Room>
<Name>2014</Name>
<Location>1-23-1</Location>
</Room>
- 如果归并上述两个内容信息,会产生冲突
<Schooler>
<Name>LiuYing</Name>
<Age>23</Age>
<Name>2014</Name>
<Location>1-23-1</Location>
</Schooler>
- 为了避免冲突,需要给可能冲突元素添加命名空间
- xmlns: xml name space 的缩写
<Schooler xmlns:student="http://my_student" xmlns:room="http://my_room">
<student:Name>LiuYing</student:Name>
<Age>23</Age>
<room:Name>2014</room:Name>
<Location>1-23-1</Location>
</Schooler>
# XML访问
## 读取
- XML读取分两个主要技术,SAX, DOM
- SAX(Simple API for XML):
- 基于事件驱动的API
- 利用SAX解析文档设计到解析器和事件处理两部分
- 特点:
- 快
- 流式读取
- DOM
- 是W3C规定的XML编程接口
- 一个XML文件再缓存中以树形结构保存,读取
- 用途
- 定位浏览XML任何一个节点信息
- 添加删除相应内容
- minidom
- minidom.parse(filename):加载读取的xml文件, filename也可以是xml代码
- doc.documentElement:获取xml文档对象,一个xml文件只有一个对于的文档对象
- node.getAttribute(attr_name):获取xml节点的属性值
- node.getElementByTagName(tage_name):得到一个节点对象集合
- node.childNodes:得到所有孩子节点
- node.childNodes[index].nodeValue:获取单个节点值
- node.firstNode:得到第一个节点,等价于node.childNodes[0]
- node.attributes[tage_name]
- etree
- 以树形结构来表示xml
- root.getiterator:得到相应的可迭代的node集合
- root.iter
- find(node_name):查找指定node_name的节点,返回一个node
- root.findall(node_name):返回多个node_name的节点
- node.tag: node对应的tagename
- node.text:node的文本值
- node.attrib: 是node的属性的字典类型的内容
- xml文件写入
- 更改
- ele.set:修改属性
- ele.append: 添加子元素
- ele.remove:删除元素
- 生成创建
- SubElement
- minidom 写入
- etree创建
一个xml文件示例:
<?xml version="1.0" encoding="utf-8" ?>
<System>
<Device desc="ControlDevice" size="normal">
<Type>computer</Type>
<Cpu Detail="The type of cpu">core_i7</Cpu>
<Gpu>6G</Gpu>
</Device>
<Camera>
<Type Other="可见光">Visible</Type>
<Size Detail="The size of get picture">240000</Size>
</Camera>
<Camera>
<Type>Infrared</Type>
<Size>120000</Size>
</Camera>
</System>
示例代码及结果:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 使用minidom打开xml文件
import xml.dom.minidom
DOMTree = xml.dom.minidom.parse("device.xml")
#得到文档对象
doc = DOMTree.documentElement
# 显示子元素
for ele in doc.childNodes:
if ele.nodeName == "Device":
print("-------Node:{0}-----".format(ele.nodeName))
childs = ele.childNodes
for child in childs:
if child.nodeName == "Type":
# data是文本节点的一个属性,表示他的值
print("Type: {0}".format(child.childNodes[0].data))
if child.nodeName == "Gpu":
# data是文本节点的一个属性,表示他的值
print("Gpu: {0}".format(child.childNodes[0].data))
if child.nodeName == "Cpu":
# data是文本节点的一个属性,表示他的值
print("Cpu: {0}".format(child.childNodes[0].data))
if child.hasAttribute("Detail"):
print("Cpu-detail: {0}".format(child.getAttribute("Detail")))
'''
运行结果:
-------Node:Device-----
Type: computer
Cpu: core_i7
Cpu-detail: The type of cpu
Gpu: 6G
'''
# 使用etree打开xml文件
import xml.etree.ElementTree
root = xml.etree.ElementTree.parse("device.xml")
print("利用getiterator访问:")
nodes = root.getiterator()
for node in nodes:
print("{0}--{1}".format(node.tag, node.text))
print("利用find和findall方法:")
ele_device = root.find("Device")
print(type(ele_device))
print("{0}--{1}".format(ele_device.tag, ele_device.text))
ele_camera = root.findall("Camera")
print(type(ele_camera))
for ele in ele_camera:
print("{0}--{1}".format(ele.tag, ele.text))
for cam in ele.getiterator():
if cam.tag =="Type":
if "Other" in cam.attrib.keys():
print(cam.attrib['Other'])
'''
运行结果:
利用getiterator访问:
System--
Device--
Type--computer
Cpu--core_i7
Gpu--6G
Camera--
Type--Visible
Size--240000
Camera--
Type--Infrared
Size--120000
利用find和findall方法:
<class 'xml.etree.ElementTree.Element'>
Device--
<class 'list'>
Camera--
可见光
Camera--
'''
自行封装的函数(该部分内容从之前写的程序中抽出的):
#读取并解析xml文件
def read_xml(in_path):
tree = ET.parse(in_path)
return tree
#将xml文件写出
def write_xml(tree, out_path):
tree.write(out_path)
#查找某个路径匹配的所有节点 tree: xml树 path: 节点路径
def find_nodes(tree, path):
root = tree.getroot()
nodes = root.findall(path)
return nodes
#判断某个节点是否包含传入参数属性 node:节点 kv_map:属性及属性值组成的map
def if_match(node, kv_map):
for key in kv_map:
if node.get(key) != kv_map.get(key):
return False
return True
#根据属性及属性值定位节点 nodelist: 节点列表 kv_map: 匹配属性及属性值
def get_node_by_keyvalue(nodelist, kv_map):
result_nodes = []
for node in nodelist:
if if_match(node, kv_map):
result_nodes.append(node)
return result_nodes
#修改/增加 /删除 节点的属性及属性值 nodelist: 节点列表 kv_map:属性及属性值
def change_node_properties(nodelist, kv_map, is_delete=False):
for node in nodelist:
for key in kv_map:
if is_delete:
if key in node.attrib:
del node.attrib[key]
else:
node.set(key, kv_map.get(key))
#1. 读取xml文件
tree = read_xml('./devicestatus.xml')
#2. 属性修改
#A. 找到父节点
nodes = find_nodes(tree, 'device')
#for i in nodes:
# print i.tag, i.attrib
#B. 通过属性准确定位子节点
result_nodes = get_node_by_keyvalue(nodes, {"name":"CarEleStatus"})
#print result_nodes[0].tag, result_nodes[0].attrib
#C. 修改节点属性
change_node_properties(result_nodes, {"value": str(carelevalue)})
#3. 输出到结果文件
write_xml(tree, "./devicestatus.xml")
二、json
json的一些基础内容:
- 在线工具
- https://www.sojson.com/
- http://www.w3school.com.cn/json/
- http://www.runoob.com/json/json-tutorial.html
- JSON(JavaScriptObjectNotation)
- 轻量级的数据交换格式,基于ECMAScript
- json格式是一个键值对形式的数据集
- key: 字符串
- value:字符串,数字,列表,json
- json使用大括号包裹
- 键值对直接用都好隔开
student={
"name": "wangdapeng",
"age": 18,
"mobile":"13260446055"
}
- json和python格式的对应
- 字符串:字符串
- 数字:数字
- 队列:list
- 对象:dict
- 布尔值:布尔值
- python for json
- json包
- json和python对象的转换
- json.dumps():对数据编码,把python格式表示成json格式
- json.loads(): 对数据解码,把json格式转换成python格式
- python读取json文件
- json.dump(): 把内容写入文件
- json.load(): 把json文件内容读入python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import json
obj2 = {'Type':('Notebook_2'),'Cpu':('i5'),'Gpu':("GTX1070"),'Id':(2)}
obj3 = {'Type':('Notebook'),'Cpu':('i5'),'Gpu':("GTX1080"),'Id':(3)}
with open('json.txt', 'r') as fp:
load_dict = json.load(fp)
tasksinfo = load_dict["Computer"]
tasksinfo.append(obj2)
tasksinfo.append(obj3)
load_dict["Computer"] = tasksinfo
with open("json.txt","w") as dump:
json.dump(load_dict, dump, indent=4)
代码执行前json:
{
"System": "",
"Computer": [
{
"Type": "Notebook",
"Detail": "A new computer",
"Cpu": "core_i7",
"Gpu": "GTX1060",
"Id": "1"
}
]
}
代码执行后json:
{
"System": "",
"Computer": [
{
"Type": "Notebook",
"Detail": "A new computer",
"Cpu": "core_i7",
"Gpu": "GTX1060",
"Id": "1"
},
{
"Type": "Notebook_2",
"Cpu": "i5",
"Gpu": "GTX1070",
"Id": 2
},
{
"Type": "Notebook",
"Cpu": "i5",
"Gpu": "GTX1080",
"Id": 3
}
]
}