XML的解析与反解析
Universal Parser 基于 xml.parsers.expat
Python内置库编写,expat
相比其它库就一个字 快
!
本页用到的XML数据
(点击此处展开)查看XML数据
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:aq="aquatic">
<animals>
<animal type="dog" id="1">
<!-- 大黄是人类的朋友 -->
<name>大黄</name>
<age>11</age>
<sex>male</sex>
<desc><![CDATA[<大黄><一只狗>]]></desc>
</animal>
<animal type="dog" id="2">
<!-- 小白是个傻狗 -->
<name>小白</name>
<age>1</age>
<sex>female</sex>
<desc>小白身上有斑点</desc>
</animal>
<animal type="cat" id="01">
<name>小卡</name>
<age>3</age>
<sex>female</sex>
<desc>小卡是只咖啡猫</desc>
</animal>
</animals>
<version>0.0.1</version>
<aq:halobios>
<!-- 海洋生物 -->
<desc>美丽的大海</desc>
</aq:halobios>
<text name="test_text">123456</text>
<text2 name="test_text2"><![CDATA[我是CDATA]]></text2>
</root>
取代xmltodict
如果您不需要此框架提供的丰富操作接口,只需要 XML
转 Pyton字典
的功能,则仅需调用 XmlParser.parse_odict
即可。
您将获得一个纯 OrderedDict
数据包,不包含任何操作。数据包的结构类似于 xmltodict
解析出来的内容,但会比 xmltodict
更加完整一点
,同样会记住所有节点的位置。
from UniversalParser import XmlParser
from UniversalParser.xml.unparser import unparse_dict
import UniversalParser as UP
docs, xml_declare = XmlParser.parse_odict(
xml_data
, encoding = None # 编码,默认 None
, namespace_separator = None # 命名空间解析(暂时不建议使用)
, attr_prefix = UP.ATTR_PREFIX # 属性前缀,默认 '@'
, cdata_key = UP.CDATA_KEY # 解析时文本域索引名,默认 '#text'
, cdata_self_key = UP.CDATA_SELF_KEY # 索引时文本域索引名,默认 'text_'
, comment_key = UP.COMMENT_KEY # 注释索引名,默认 '#comment'
, combine_cdata = True # 是否合并 文本 和 CDATA
, include_comment = False # 是否解析注释
, cdata_separator = UP.CDATA_SEPARATOR # 同一节点多行文本地连接符,默认空格
, loc_key = UP.LOC_KEY # 位置属性名,默认 '__loc__'
, include_loc = False # 是否解析时包含位置信息,默认不包含,即默认没有'__loc__'属性
)
import json
print(json.dumps(docs, ensure_ascii=False, indent=4))
'''反向解析(即 JSON 转 XML 功能)
请确保 `XmlParser.parse_odict` 和 `unparse_dict` 参数传递的一致性,只有保证了一致性,才能够 1:1 还原初始XML。
'''
print(unparse_dict(docs, xml_declare, out_stream=None))
读XML
import UniversalParser as UP
# 假设上面的 XML 数据存放在 xml_data 变量中
xmlManager = UP.parse_xml(xml_data)
# 假设上面的 XML 数据存放在 data.xml 文件中
xmlManager = UP.parse('data.xml', encoding='utf-8')
analysis_text
- 是否对文本域建立索引,默认 True 。设置为 True ,可以使用与文本域相关的所有方法,但在不需要文本域索引的情况下,建议设置为 False;combine_cdata
- 是否将 CDATA 视为文本域处理,默认 True 。设置为 False 后,文本域将不包含 CDATA 内容,CDATA 内容将被独立解析;open_cdata
- 是否对 CDATA 单独建立索引,默认 False 。如需开启,必须同时设定 open_cdata=True 和 combine_cdata=False;include_comment
- 是否包含注释,默认 False;open_comment
- 是否对注释建立索引,默认 False。如需启用,必须同时设定 include_comment=True 和 open_comment=True;cdata_separator
- 当节点内文本域被拆成多行时的连接符,默认空格连接;analysis_mode
- 选择解析引擎,默认非递归算法,如需使用递归算法请赋值为 AnalysisMode.RECURSION_OLD;include_loc
- 是否需要包含原XML中各个节点的位置信息,默认 True。设置为 False 将导致反向解析生成的 XML 是乱序的;attr_prefix
- 属性解析前缀,默认"@",非必要不修改;namespace_separator
- 启用命名空间解析,默认 None (当前版本不建议使用);cdata_key
- 文本域解析名,默认"#text",非必要不修改;real_cdata_key
- 文本域索引名,默认"text_",非必要不修改;cdata_self_key
- CDATA 的解析及索引名,默认"#CDATA",非必要不修改;comment_key
- 注释的解析及索引名,默认"#comment",非必要不修改;
注意: 若XML中存在 CDATA
,且其中存在需要在XML中转义的字符时,必须同时开启参数:combine_cdata=False
和 open_cdata=True
,必须!!!。
链式属性
【我更喜换用此功能获取属性值或者文本域,而不是用于路径查找。】
XML 中的每一个 标签名
和 属性名
均会被视为 UniversalParser对象的属性
。因此可以通过 .
运算符获取数据。但也因此产生了一定的局限性,当同一个节点的 孩子节点标签名
和 该节点的属性名
冲突时,该节点的属性将会丢失,此问题会在未来的版本中做适当处理。
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
, open_comment = True
, include_comment = True
)
'''旧版本中 xmlManager.xml 和 xmlManager.document 完全等价
'''
root = xmlManager.document.root
'''text_ 来自于 real_cdata_key 传入的值,默认是 text_
'''
print(root.text_) # 每一个节点均有文本域,为空就是空字符串
'''任何一个节点都可以用属性的方式进行访问
但注意,类似 copy 之类的标签名,会引发内部变量名冲突,此时需要用 node['copy'] 获取
'''
animals = root.animals.animal
'''严格按照原来 XML 中的顺序进行输出
'''
print([_a.name for _a in animals]) # ['大黄', '小白', '小卡']
print([_a.type for _a in animals]) # ['dog', 'dog', 'cat']
使用链式属性查找的几个注意点
【情形一】:
<a>
<b>0</b>
</a>
xmlManager.document.a.b
获取到的是 ChainDict
类型。【情形二】:
<a>
<b>0</b>
<b>1</b>
<b>2</b>
</a>
xmlManager.document.a.b
获取到的是 List[ChainDict]
类型。
文本域
链式属性查找
下的文本域获取
import UniversalParser as UP
from UniversalParser import SM
xmlManager = UP.parse_xml(xml_data)
root = xmlManager.document.root
'''当节点只有文本域没有属性时,获取的将直接是文本域内容
'''
print(root.version) # 0.0.1
# 当唯一标签名称节点存在属性时,获取的值是 ChainDict 类型
# ChainDict 类型有下列四种获取文本域的方式
'''一、通过 find_text 方法获取
'''
print(xmlManager.find_text(root.text)) # 123456
'''二、内置属性名获取(不推荐,可能会藏有版本迁移失效的问题)
'''
print(root.text.text_) # 123456。text_ 来自于 real_cdata_key 设置的值
'''三、魔法操作符获取
'''
print(root.text & SM.text) # 123456
'''四、格式化获取
'''
def format_func(s):
return '二次处理:' + s
print(xmlManager.find_text(root.text, format_func)) # 二次处理:123456
快速定位方法
查找后的文本域获取
# 快速定位查找获取到的类型只有两种:List[ChainDict] 和 ChainDict
import UniversalParser as UP
from UniversalParser import SM
xmlManager = UP.parse_xml(xml_data)
# 下面两种获取节点的方式完全等价(内部均调用了 find_nodes_by_tag 方法)
version = (xmlManager | 'version') ^ 1 # | 限制标签名,^ 限制输出个数(具体请看【魔法定位】)
version = xmlManager.find_nodes_by_tag('version', one_=True)
'''和链式查找的节点唯一不同的就是:
【链式查找】的节点:当节点只有文本域没有属性时,获取的将直接是文本域内容。
而【快速定位方法】查找到的节点没有这种特性,其它获取方式均一致。
'''
print(xmlManager.find_text(version)) # 0.0.1
print(version.text_) # 0.0.1
print(version & SM.text) # 0.0.1
def format_func(s):
return '二次处理:' + s
print(xmlManager.find_text(version, format_func)) # 二次处理:0.0.1
'''特别地,可以直接通过属性获取文本域
'''
text = xmlManager.find_text_by_attrs(name="test_text")
print(text) # 123456
find_text_by_attrs
方法还接受两个关键字参数:text_type
和 format_func
。
text_type
目前有三种取值:TextType.STR、TextType.FLOAT、TextType.INT。format_func
参数的用法,参考find_text
函数。
单独获取 CDATA
的值
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data, open_cdata=True, combine_cdata=False)
animals = xmlManager | 'animal' # ChainManager 内部实现了 __getitem__ 方法
print(animals[0].desc.text_) # 输出空字符串
print(animals[0].desc[UP.CDATA_SELF_KEY]) # ['<大黄><一只狗>']
获取 注释
内容
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data, include_comment=True, open_comment=True)
animals = list(xmlManager | 'animal')
print(animals[1][UP.COMMENT_KEY]) # ['小白是个傻狗']
快速定位查找
属性定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data)
'''根据属性获取唯一的节点
若无任何符合条件的节点,抛出 NoNodesFound 异常
若超过一个符合条件的节点,抛出 MoreNodesFound 异常
'''
node = xmlManager.find_nodes_by_attrs(name="test_text", one_=True)
node = xmlManager.find_node_by_attrs(name="test_text") # 完全等价于上一行语句
print(node & UP.SM.text) # 123456
'''根据属性获取多个符合条件节点
'''
nodes = xmlManager.find_nodes_by_attrs(name="test_text")
print(nodes[0] & UP.SM.text) # 123456
'''多个属性同时限制查找(且的关系,必须同时满足)
'''
nodes = xmlManager.find_nodes_by_attrs(name="test_text", other="ttt")
print(len(nodes)) # 0
属性定位+获取指定位置
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data)
'''第一个参数是要获取的索引集,非法的索引将被忽略
'''
dogs = xmlManager.find_nodes_by_indexs([1,], type="dog")
print(dogs[0].name) # 小白
标签名定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data)
nodes = xmlManager.find_nodes_by_tag('animal', one_=False)
print(len(nodes)) # 3
文本域定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data)
'''注意
当 open_cdata=False 且 combine_cdata=True 时,CDATA将出现在排查范围内
'''
node = xmlManager.find_nodes_by_text('123456', one_=True)
print(node.name) # test_text
CDATA定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data, open_cdata=True, combine_cdata=False)
node = xmlManager.find_nodes_by_cdata('<大黄><一只狗>', one_=True)
print((node & UP.SM.parent).name) # 大黄
ChainDict
类型用 &
运算符,后面跟 UP.SM.parent
表示获取其父节点。
注释内容定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data, open_comment=True, include_comment=True)
node = xmlManager.find_nodes_by_comment('海洋生物', one_=True)
print(node.desc) # 美丽的大海
树结构关系定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data)
# 获取 animals 标签下的 'animal' 标签,限制 'animal' 标签文本域为空
# 有名为 id 的属性,且属性值为 '01'
# 方法一:
limit_node = (xmlManager | 'animals') ^ 1
animal = xmlManager.find_nodes_with_ancestor(
unique_node = limit_node
, tag_ = 'animal' # 带查找节点的标签名限制
, text_ = '' # 待查找节点的文本域限制
, find_func = xmlManager.find_nodes_by_tag_text_attrs # 指定内部查找的函数类型
, constraint = None # 限制节点的传参数据包
, one_ = True # 限制有且仅有一个输出
, id = '01' # 待查找节点的属性限制,find_func 参数指向的方法必须要支持属性查找
)
print(animal.name) # 小卡
# 方法二:(完全等价于方法一)
animal = xmlManager.find_nodes_with_ancestor(
constraint = {
'args': [], 'kwargs': {'tag_': 'animals'}
}
, tag_ = 'animal'
, text_ = ''
, find_func = xmlManager.find_nodes_by_tag_text_attrs
, one_ = True
, id = '01'
)
print(animal.name) # 小卡
constraint
和 unique_node
只允许选其一使用。
find_nodes_with_ancestor
- 用祖先节点限制查找,换句话说。unique_node
或 constraint
所指定的节点只要存在于待查找节点的
祖先节点范围内,该节点就会被找到。
同样的方法,目前还有两个:
find_nodes_with_descendants
和 find_nodes_with_sibling_ancestor
,前者是子孙节点限制查找,后者是祖先及其兄弟节点限制查找。
参数和注意点完全和 find_nodes_with_ancestor
一致。
【祖先及其兄弟节点】一词的解释:
若a
节点有一个父节点为b
,b
有两个兄弟节点(c
, d
)。
- 假设
b
为根节点,则a
的祖先及其兄弟节点就是(b
,c
,d
)。 - 假设
b
不是根节点,b
有一个父节点m
(m
为根节点),c
的父节点为h
,h
有一个兄弟节点r
,m
有两个兄弟节点(u
,k
)。则a
节点的祖先及其兄弟节点 就是[(b
,c
,d
), (m
,u
,k
)]。如您所见,它是一层一层往上搜索的,只要在某一层找到符合条件的节点,就停止搜索,否则会遍历完所有的相关节点。
混合定位
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data)
'''标签 + 属性定位
'''
nodes = xmlManager.find_nodes_by_tag_and_attrs(
tag_ = 'text'
, name = 'test_text'
, one_= False
)
print(nodes[0] & UP.SM.text) # 123456
'''标签 + 文本域 + 属性定位
'''
nodes = xmlManager.find_nodes_by_tag_text_attrs(
tag_ = 'text'
, text_ = '1234567'
, name = 'test_text'
, one_= False
)
print(len(nodes)) # 0
tag
标签名,这里使用 tag_
作为参数名称,其它参数名称取名均有此约束。(仅混合定位时需要注意)
增删改操作
插入新节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animals = (xmlManager | 'animals') ^ 1
insert_node = xmlManager.insert(
animals
, tag = 'animal'
, attrs = {'type':'cat'}
, text = ''
)
print(insert_node.type) # cat
xmlManager.save_as_xml()
插入XML字符串
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animals = (xmlManager | 'animals') ^ 1
insert_xml = '''
<animal type="cat" id="02">
<name>小蝶一号</name>
<age>0.5</age>
<sex>Unkown</sex>
<desc>啥子</desc>
</animal>
'''
xmlManager.insert_xmlstring(animals, insert_xml) # 尾插到该节点
xmlManager.save_as_xml()
复制/拷贝节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
root = xmlManager.xml.root
animals = (xmlManager | 'animals') ^ 1
# 将 animals 拷贝一份到 root.text 中
xmlManager.copy_to(root.text, animals)
xmlManager.save_as_xml()
ChainDict
和 List[ChainDict]
之外,还支持任意 dict
类型和 list
类型的组合。
插入新的属性
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data, open_cdata=True, combine_cdata=False)
node = xmlManager.find_nodes_by_tag('animals', one_=True)
print(node & UP.SM.attr_names) # ['__loc__']
xmlManager.insert_attrs(node, name='插入')
print(node & UP.SM.attr_names) # ['__loc__', 'name']
__loc__
属性每一个节点都会在解析时添加进去,目的是为了记录节点的位置信息。
插入注释
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
, open_comment = True
, include_comment = True
)
xmlManager.insert_comment(xmlManager.xml.root, '新的注释')
xmlManager.save_as_xml()
include_comment=True
和 open_comment=True
。
插入CDATA
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
xmlManager.insert_cdata(xmlManager.xml.root, '新的CDATA')
xmlManager.save_as_xml()
CDATA
参数开启,即:combine_cdata=False
和 open_cdata=True
。
移动节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
root = xmlManager.xml.root
animals = (xmlManager | 'animals') ^ 1
# 将 animals 从 root 移动到 root.text
xmlManager.move(animals, root.text)
xmlManager.save_as_xml()
交换节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
root = xmlManager.xml.root
animals = (xmlManager | 'animals') ^ 1
# 将两个节点互换(包括位置信息)
xmlManager.swap(animals, root.text)
xmlManager.save_as_xml()
平移节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
root = xmlManager.xml.root
animals = (xmlManager | 'animals') ^ 1
# 向上平移
xmlManager.pan_up(animals.animal[2], 2) # 向上平移2个单位
# 平移到最顶部
xmlManager.pan_up(animals.animal[2], top=True)
# 向下平移
xmlManager.pan_down(animals.animal[0], 2) # 向下平移2个单位
# 平移到最底部
xmlManager.pan_down(animals.animal[0], bottom=True)
xmlManager.save_as_xml()
<a></a>
节点有三个孩子节点<b>1</b>
、<b>2</b>
、<c>1</c>
。则 b1
向下平移2个单位后,顺序不会变成 <b>2</b>
、<c>1</c>
、<b>1</b>
,而是 <b>2</b>
、<b>1</b>
、<c>1</c>
。这是因为同类型的节点平移,只会在用类型节点下生效(若您需要此方法,请您务必了解此细节)。
平移的步数必须 >= 0,如果步数超过了节点的总数,则默认移动到端点,向上平移就是移动到最上方,向下平移就是移动到最下方。
清空文本域
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
name = ((xmlManager @ {'id': '1'}) | 'name') ^ 1
xmlManager.clear_text(name)
'''批量清空
'''
xmlManager.batch_clear_text([name, ])
xmlManager.save_as_xml()
name=''
这样的清空方式。
清空节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
root = xmlManager.xml.root
# 清空属性
xmlManager.clear_node_attrs(root.text)
# 清空文本域及所有的孩子节点
xmlManager.clear_node_content(root.text)
# 上述两种方法的结合
xmlManager.clear_node(root.text)
xmlManager.save_as_xml()
删除节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data, open_cdata=True, combine_cdata=False)
'''方法一
'''
animal = (xmlManager @ {'id': '01'}) ^ 1 # @ 表示属性限制
xmlManager.popitem(animal) # 删除 id = '01' 的 animal 节点
'''方法二
'''
xmlManager.pop_node_by_attrs(id="01") # 该节点必须只能被唯一查找
xmlManager.pop_nodes_by_attrs(type="dog")
'''输出为 XML
'''
xmlManager.save_as_xml('ttt.xml')
del
,还包括删除自身的所有相关索引,以及子孙节点的相关索引。请务必使用本工具提供的方法删除,否则数据无法在运行时及时更新,严重可能导致数据关系混乱。
删除节点属性
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = xmlManager.find_nodes_by_tag('animal')[0]
print(animal & UP.SM.attr_names) # ['__loc__', 'type', 'id']
print(animal.type) # dog
del_attr: Tuple[str, str] = xmlManager.del_attr(animal, 'type')
print(animal & UP.SM.attr_names) # ['__loc__', 'id']
'''批量删除属性
删除不存在的属性时会报错。
'''
del_attrs: List[Tuple[str, str]] = xmlManager.batch_del_attr(animal, 'type', 'id')
xmlManager.save_as_xml()
修改节点属性值
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = xmlManager.find_nodes_by_tag('animal')[0]
print(animal.type) # dog
old_attr: Tuple[str, str] = xmlManager.update_attr(animal, "type", "old_dog")
print(animal.type) # old_dog
'''批量修改属性值
下面两个方式功能上一模一样。
'''
old_attrs: List[Tuple[str, str]] = xmlManager.batch_update_attrs(animal, type="old_dog")
old_attrs: List[Tuple[str, str]] = xmlManager.batch_update_attrs(animal, ("type", "old_dog"))
xmlManager.save_as_xml()
修改节点文本域
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
name = ((xmlManager @ {'id': '1'}) | 'name') ^ 1
print(name & UP.SM.text) # 大黄
old_text = xmlManager.update_text(name, '大黄二号')
print(name & UP.SM.text) # 大黄二号
xmlManager.save_as_xml()
树结构操作
注意: 当节点是拷贝的或者通过字符串插入的,请勿使用 &
运算符。
获取父节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = list(xmlManager | 'animal')[1]
# 下面三行完全等价
print(xmlManager.get_parent(animal))
print(xmlManager.objects.get_parent(animal))
print(animal & UP.SM.parent)
print(xmlManager.get_parent(animal) == animal & UP.SM.parent) # True
# 另类获取方式
# 先取父节点的 id,然后再取父节点
p_id = xmlManager.objects.parent(id(animal))
print(xmlManager._id_nodes[p_id] == animal & UP.SM.parent) # True
获取祖先节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = list(xmlManager | 'animal')[1]
# 下面两行完全等价
print(xmlManager.get_ancestor(animal))
print(animal & UP.SM.ancestor)
获取孩子节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = list(xmlManager | 'animal')[1]
# 下面两行完全等价
print(xmlManager.get_children(animal))
print(animal & UP.SM.children)
获取子孙节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = list(xmlManager | 'animal')[1]
# 下面两行完全等价
print(xmlManager.get_descendants(animal))
print(animal & UP.SM.descendants)
获取兄弟节点
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
animal = list(xmlManager | 'animal')[1]
# 下面两行完全等价
print(xmlManager.get_siblings(animal))
print(animal & UP.SM.siblings)
获取运行时数据
获取初始XML
转OrderedDict
的字典数据
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
print(xmlManager.objects.doc)
获取运行时的最新XML字符串
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
print(xmlManager.get_xml_data())
获取运行时的JSON字典数据
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
print(xmlManager.document)
print(xmlManager.xml) # 旧版本变量,尽量保持向后兼容
print(xmlManager.document == xmlManager.xml) # True
反向解析/另存为
另存为XML
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
xmlManager.save_as_xml(
path = 'output.xml' # 输出路径
, encoding = 'utf-8' # 编码格式
, distinct = True # 是否对同一节点内的 注释 和 CDATA 去重
)
另存为JSON
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
xmlManager.save_as_json(
path = 'output.json' # 输出路径
, encoding = 'utf-8' # 编码格式
, ensure_ascii = True # 输出 ASCII 编码
, indent = None # 缩进
, ori = False # 是否保留初始标记,如 #text 等
)
魔法定位(替换XPath)
魔法操作符
操作符 | 对应方法 | 操作对象 | 参数 | 返回值类型 |
---|---|---|---|---|
& |
- | ChainDict | UniversalParser.SM 对象 |
Union[str, ChainDict, List[ChainDict]] |
@ |
find_nodes_by_attrs |
ChainManager | Union[dict, str] |
ChainManager |
| |
find_nodes_by_tag |
ChainManager | str |
ChainManager |
^ |
- | ChainManager | int |
Union[ChainDict, List[ChainDict]] |
/ |
find_nodes_by_text |
ChainManager | Any |
ChainManager |
// |
find_nodes_by_comment |
ChainManager | str |
ChainManager |
% |
find_nodes_by_cdata |
ChainManager | str |
ChainManager |
【注意】:ChainManager
本身就是一个可迭代对象,认识这点,将帮助您减少部分代码量。
从上表可以很轻易的掌握一些高级用法,比如操作符可以连续
使用,但需要注意的是,原先的操作符是有
优先级
的,所以进行运算时,必须要在两侧加括号,如:(manager @ {'id': '01'}) | 'tag'
,表示查找id
属性为
01
且标签为tag
的节点。
^
运算符的作用相当于切片,右操作数必须为大于0的整数。特别地,右操作数为1时,将限制有且仅有一个输出,否则报错。
如:manager ^ 1
当有且仅有一个输出时等价于 list(manager)[0]
。
|
运算符的右操作数是 标签名
字符串。比如 <a>123</a>
,要查找的话就是 manager | 'a'
。
/
运算符的右操作数是 文本域
字符串。
//
运算符的右操作数是 注释
字符串,使用此运算符必须要开启注释功能!
%
运算符的右操作数是 CDATA
字符串,必须开启CDATA功能后才能使用!
特别地,对 @
运算符,当限制属性有且仅有一个时,Universal Parser 提供了一种简写方式:
import UniversalParser as UP
xmlManager = UP.parse_xml(xml_data
, combine_cdata = False
, open_cdata = True
)
xmlManager.SEARCH_ATTR_KEY = 'id' # 默认就是 'id'
animal1 = list(xmlManager @ '01')[0] # 简写,可以忽略键
animal2 = list(xmlManager @ {'id': '01'})[0]
print(animal1 == animal2) # True
xmlManager.SEARCH_ATTR_KEY
,它是灵活的。