mini_toolbox.xml 源代码
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# 用于XML相关操作
__all__ = ['XmlTools']
from lxml import etree
from lxml.etree import _Comment as CM
from lxml.etree import _Element as EM
from lxml.etree import _ElementTree as ET
from typing import List, Optional, Union
[文档]class XmlTools():
""" 用于XML相关操作
Args:
file (str): xml文件路径
pretty (bool): 是否格式化保存, 默认为原始格式
Note:
官方文档: `Element`_, `ElementTree`_, `xpath`_, `findall`_
Example:
lxml.etree 语法示例::
# 节点间位置关系约定: 父节点, 子节点, 上节点(同级), 下节点(同级)
node.findall(xpath) # 搜索 # 查找全部匹配子节点, 返回列表
node.find(xpath) # 搜索 # 查找首个匹配子节点, 返回节点
node.iter() # 遍历 # 递归遍历子节点
node.itertext() # 遍历 # 递归遍历子节点文本
node.iterchildren() # 遍历 # 遍历子节点
node.getparent() # 获取 # 获取父节点
node.getprevious() # 获取 # 获取上节点
node.getnext() # 获取 # 获取下节点
root.makeelement(name) # 获取 # 创建节点
node.append(_node) # 增删 # 新增子节点
node.addprevious(_node) # 增删 # 新增上节点
node.addnext(_node) # 增删 # 新增下节点
node.remove(_node) # 增删 # 删除节点
node.tag # 编辑 # 节点标签
node.text # 编辑 # 节点文本
node.attrib # 编辑 # 节点属性
node.get(key) # 编辑 # 节点属性获取
node.set(key, value) # 编辑 # 节点属性增改
node.findall(xpath) 简化语法示例::
. # 当前节点
.. # 父节点
* # 下级全部子节点
// # 递归全部子节点
[@key] # 节点存在指定属性
[@key='value'] # 节点存在指定键值对
[tag] # 子节点存在指定标签
[tag='text'] # 子节点存在指定标签文本对
.. _Element:
https://lxml.de/2.2/api/lxml.etree._Element-class.html
.. _ElementTree:
https://lxml.de/2.2/api/lxml.etree._ElementTree-class.html
.. _xpath:
https://docs.python.org/3/library/xml.etree.elementtree.html#supported-xpath-syntax
.. _findall:
https://lxml.de/FAQ.html#what-are-the-findall-and-xpath-methods-on-element-tree
"""
def __init__(self, file: str, pretty: bool = False) -> None:
self.file = file
self.pretty = pretty
if self.pretty:
parser = etree.XMLParser(remove_blank_text=True)
self.tree: ET = etree.parse(file, parser=parser)
else:
self.tree: ET = etree.parse(file)
self.root: EM = self.tree.getroot()
self.nsmap = self.root.nsmap
[文档] def save(self, as_file: Optional[str] = None, encoding: str = 'utf-8', indent: str = ' ') -> None:
""" 保存或另存为文件
Args:
as_file (str): 另存为文件, 默认为更新原始文件
encoding (str): 文件编码, 默认为utf-8
indent (str): 格式化保存时缩进, 默认为4个空白字符
"""
if self.pretty:
etree.indent(self.tree, indent)
self.tree.write(as_file or self.file, encoding=encoding, pretty_print=self.pretty, xml_declaration=True)
def _seq_nodes(self, nodes: Union[EM, List[EM]], reverse: bool) -> List[EM]:
""" 仅内部调用, 序列化nodes节点 """
if not isinstance(nodes, list):
nodes = [nodes]
if reverse:
nodes.reverse()
return nodes
[文档] def findall(self, xpath: str) -> List[EM]:
""" 查找全部匹配子节点, 返回节点列表 """
return self.root.findall(xpath, namespaces=self.nsmap)
[文档] def find(self, xpath: str) -> Optional[EM]:
""" 查找首个匹配子节点, 返回节点 """
_t = self.findall(xpath)
return _t[0] if _t else None
[文档] def getprev(self, node: EM) -> Optional[EM]:
""" 获取上节点 """
if node is not None:
return node.getprevious()
[文档] def getnext(self, node: EM) -> Optional[EM]:
""" 获取下节点 """
if node is not None:
return node.getnext()
[文档] def getparent(self, node: EM) -> Optional[EM]:
""" 获取父节点 """
if node is not None:
return node.getparent()
[文档] def addchild(self, base: EM, nodes: Union[EM, List[EM]], reverse: bool = False) -> EM:
""" 新增子节点, 支持同时添加多个, 返回base节点 """
for node in self._seq_nodes(nodes, reverse):
base.append(node)
return base
[文档] def addprev(self, base: EM, nodes: Union[EM, List[EM]], reverse: bool = False) -> EM:
""" 新增上节点, 支持同时添加多个, 返回base节点 """
for node in self._seq_nodes(nodes, reverse):
base.addprevious(node)
return base
[文档] def addnext(self, base: EM, nodes: Union[EM, List[EM]], reverse: bool = False) -> EM:
""" 新增下节点, 支持同时添加多个, 返回base节点 """
for node in self._seq_nodes(nodes, reverse):
base.addnext(node)
return base
[文档] def remove(self, node: EM) -> None:
""" 删除节点 """
parent = self.getparent(node)
if parent is not None:
parent.remove(node)
[文档] def gettag(self, node: EM) -> Optional[str]:
""" 节点标签获取 """
if node is not None:
return node.tag
[文档] def gettext(self, node: EM) -> Optional[str]:
""" 节点文本获取 """
if node is not None:
return node.text
[文档] def getattr(self, node: EM, key: str) -> Optional[str]:
""" 节点属性获取 """
if node is not None:
return node.get(str(key))
[文档] def settext(self, node: EM, text: Optional[str] = None) -> None:
""" 节点文本修改 """
if node is not None and text is not None:
node.text = str(text)
[文档] def setattr(self, node: EM, attrib: Optional[dict] = None) -> None:
""" 节点属性增改 """
if node is not None and attrib is not None:
for k, v in attrib.items():
node.set(str(k), str(v))
[文档] def makenode(self,
tag: str,
text: Optional[str] = None,
attrib: Optional[dict] = None,
namespace: Optional[str] = None) -> EM:
""" 创建节点
Args:
tag (str): 节点标签
text (str): 节点文本
attrib (dict): 节点属性
namespace (Optional[str]): 节点命名空间, 默认为None, 自动处理
"""
if namespace in self.nsmap:
node: EM = self.root.makeelement('{{{}}}{}'.format(self.nsmap[namespace], tag), nsmap=self.nsmap)
else:
node: EM = self.root.makeelement(tag, nsmap=self.nsmap)
self.settext(node, text)
self.setattr(node, attrib)
return node
[文档] def makenote(self, text: str, space: str = ' ') -> CM:
""" 创建注释, 默认文本前后添加一个空格 """
return etree.Comment('{}{}{}'.format(space, text, space))