本主题主要说明python的xml dom标准模块,主要讲解xml的两个操作:
1. 解析处理xml文档
2. 创建产生xml文档
重点搞清楚python中xml dom的结构设计。
一、 dom API结构
查看xml.dom模块,可以看到xml.dom的API结构如下。
NodeFilter # NodeFilter接口,仅仅提供常量定义
domreg # 不直接使用,提供DOM实现访问接口。
expatbuilder #
minicompat
minidom # dom的实现方式之一
pulldom # dom的实现方式之二
xmlbuilder
定义了Node结构
Node
UserDataHandler
1. domreg
两个核心函数获取与注册实现工厂。
getDOMImplementation(name=None, features=()) # 得到DOM实现
registerDOMImplementation(name, factory) # 注册DOM实现(需要自己实现一个DOMImplementation接口,并注册)
提供两个数据:
registered = {}
well_known_implementations = {'4DOM': 'xml.dom.DOMImplementation', 'mi...
import xml.dom
import xml.dom.domreg
# 数据
print(xml.dom.domreg.well_known_implementations)
print(xml.dom.domreg.registered)
mini_dom = xml.dom.domreg.getDOMImplementation(name='minidom')
# '4DOM'没有提供实现,下面代码会产生异常。
# four_dom = xml.dom.domreg.getDOMImplementation(name='4DOM')
print(mini_dom)
# print(four_dom)
{'minidom': 'xml.dom.minidom', '4DOM': 'xml.dom.DOMImplementation'}
{}
2. minidom模块
该模块使用help可以得到帮助,该模块下提供三个函数,负责启动解析工作。
|- getDOMImplementation(features=None)
|- parse(file, parser=None, bufsize=None)
|- 返回document对象
|- parseString(string, parser=None)
同时提供对DOM对象的封装:
|- builtins.object
|- Childless
|- CharacterData(Childless, Node)
| |- Comment
| |- Text
| |- CDATASection
|- ProcessingInstruction(Childless, Node)
|- ElementInfo
|- Identified
|- DocumentType(Identified, Childless, Node)
|- Entity(Identified, Node)
|- Notation(Identified, Childless, Node)
|- NamedNodeMap
|- ReadOnlySequentialNamedNodeMap
|- TypeInfo
节点对象:
|- xml.dom.Node(builtins.object)
|- Node
|- Attr
|- Document(Node, xml.dom.xmlbuilder.DocumentLS)
|- DocumentFragment
|- Element
DOM解析实现:
xml.dom.xmlbuilder.DOMImplementationLS(builtins.object)
|- DOMImplementation
具体每个接口的接口函数,可以通过help与dir查看到帮助。
import xml.dom
import xml.dom.minidom
import xml.sax
dom = xml.dom.minidom.parse(
'note.xml',
# parser=None, # parser = xml.sax.make_parser()或者ExpatParser对象
parser=xml.sax.make_parser(),
bufsize=1024*10)
print(dom)
3. Node与Element对象提供节点访问与遍历
| appendChild(self, node)
|
| cloneNode(self, deep)
|
| getInterface(self, feature)
|
| getUserData(self, key)
|
| hasChildNodes(self)
|
| insertBefore(self, newChild, refChild)
|
| isSameNode(self, other)
|
| isSupported(self, feature, version)
|
| normalize(self)
|
| removeChild(self, oldChild)
|
| replaceChild(self, newChild, oldChild)
|
| setUserData(self, key, data, handler)
|
| toprettyxml(self, indent='\t', newl='\n', encoding=None)
|
| toxml(self, encoding=None)
|
| unlink(self)
import xml.dom
import xml.dom.minidom
import xml.sax
dom = xml.dom.minidom.parse(
'note.xml',
# parser=None, # parser = xml.sax.make_parser()或者ExpatParser对象
parser=xml.sax.make_parser(),
bufsize=1024*10)
children = dom.childNodes
def list_nodes(nd):
if nd.nodeType == xml.dom.Node.ELEMENT_NODE:
print("标签名:", nd.tagName)
# print('节点名:', nd.nodeName)
for attr_ in nd.attributes.items():
print(attr_.name, attr_.value)
if nd.nodeType == xml.dom.Node.TEXT_NODE:
if nd.wholeText.strip() != '':
print('文本:', nd.wholeText.strip())
if nd.nodeType == xml.dom.Node.ELEMENT_NODE and nd.hasChildNodes():
for nd_ in nd.childNodes:
list_nodes(nd_)
list_nodes(children[0])
标签名: note
标签名: to
文本: Tove
标签名: from
文本: Jani
标签名: heading
文本: Reminder
标签名: body
文本: Don't forget me this weekend!
Document对象提供文档创建操作
| appendChild(self, node)
|
| cloneNode(self, deep)
|
| createAttribute(self, qName)
|
| createAttributeNS(self, namespaceURI, qualifiedName)
|
| createCDATASection(self, data)
|
| createComment(self, data)
|
| createDocumentFragment(self)
|
| createElement(self, tagName)
|
| createElementNS(self, namespaceURI, qualifiedName)
|
| createProcessingInstruction(self, target, data)
|
| createTextNode(self, data)
|
| getElementById(self, id)
|
| getElementsByTagName(self, name)
|
| getElementsByTagNameNS(self, namespaceURI, localName)
|
| importNode(self, node, deep)
|
| isSupported(self, feature, version)
|
| removeChild(self, oldChild)
|
| renameNode(self, n, namespaceURI, name)
|
| unlink(self)
|
| writexml(self, writer, indent='', addindent='', newl='', encoding=None)
数据定义:
| childNodes
|
| doctype
|
| documentElement
DocumentLS
| abort(self)
|
| load(self, uri)
|
| loadXML(self, source)
|
| saveXML(self, snode)
4. DOMImplementation接口实现
implementation = xml.dom.minidom.getDOMImplementation()
|- createDocument(self, namespaceURI, qualifiedName, doctype)
|
|- createDocumentType(self, qualifiedName, publicId, systemId)
|
|- getInterface(self, feature)
|
|- hasFeature(self, feature, version)
下面的接口没有实现
|
|- createDOMBuilder(self, mode, schemaType)
|
|- createDOMInputSource(self)
|
|- createDOMWriter(self)
由于没有实现createDOMWriter接口,所以python中xml文件的读写使用Document对象提供的writexml函数实现。
# coding = utf-8
import xml.dom
import xml.dom.minidom
implementation = xml.dom.minidom.getDOMImplementation()
print(implementation)
doc_type = implementation.createDocumentType(
qualifiedName='HH',
publicId='-//mybatis.org//DTD Config 3.0//EN',
systemId='http://mybatis.org/dtd/mybatis-3-config.dtd')
doc = implementation.createDocument(
namespaceURI='http://xmlns.jcp.org/xml/ns/javaee',
qualifiedName='HH',
doctype=doc_type)
# writer = implementation.createDOMWriter() # 没有实现,直接使用文件描述符号。
# 创建根节点
root_element = doc.createElement('myroot')
attr1 = doc.createAttribute('属性')
attr1.value = '属性值'
root_element.setAttributeNode(attr1)
# doc.documentElement只读,创建文档的时候唯一产生
doc.documentElement.appendChild(root_element)
# 产生文本子节点
txt = doc.createTextNode('我是文本')
root_element.appendChild(txt)
with open('my.xml','w') as fd:
doc.writexml(fd,
indent='\t',
addindent='\t',
newl='\n',
encoding='utf-8')
# 下面是读取产生的xml文件
with open('my.xml', 'r') as fd:
print(fd.read())
我是文本
5. 使用Document直接构造xml DOM对象
Document直接创建的对象,起产生的xml DOM不受documentURI与doctype属性影响。而且不会自动产生documentElement元素。这一点与上面createDocument函数产生的DOM是有差异的。所以建议使用上面的方式产生DOM文档。
# coding = utf-8
import xml.dom
import xml.dom.minidom
implementation = xml.dom.minidom.getDOMImplementation()
print(implementation)
doc_type = implementation.createDocumentType(
qualifiedName='HH',
publicId='-//mybatis.org//DTD Config 3.0//EN',
systemId='http://mybatis.org/dtd/mybatis-3-config.dtd')
# writer = implementation.createDOMWriter() # 没有实现,直接使用文件描述符号。
doc = xml.dom.minidom.Document()
doc.documentURI='http://xmlns.jcp.org/xml/ns/javaee'
doc.doctype=doc_type
# 创建根节点
root_element = doc.createElement('myroot')
attr1 = doc.createAttribute('属性')
attr1.value = '属性值'
root_element.setAttributeNode(attr1)
# doc.documentElement只读,创建文档的时候唯一产生
doc.appendChild(root_element)
# 产生文本子节点
txt = doc.createTextNode('我是文本')
root_element.appendChild(txt)
with open('your.xml','w') as fd:
doc.writexml(fd,
indent='\t',
addindent='\t',
newl='\n',
encoding='utf-8')
6. expatbuilder
提供基本的builder实现:
builtins.object
ElementInfo
ExpatBuilder
FragmentBuilder
InternalSubsetExtractor
FilterCrutch
Rejecter
Skipper
FilterVisibilityController
Namespaces
ExpatBuilderNS(Namespaces, ExpatBuilder)
FragmentBuilderNS(Namespaces, FragmentBuilder)
核心的是ExpatBuilder类的两个成员函数:
parseFile(self, file)
返回Document对象
parseString(self, string)
返回Document对象。
import xml.dom
import xml.dom.minidom
import xml.dom.expatbuilder
builder = xml.dom.expatbuilder.ExpatBuilder()
dom = builder.parseFile(open('note.xml', 'r'))
children = dom.childNodes
print(children)
print(dom.documentElement)
def list_nodes(nd):
if nd.nodeType == xml.dom.Node.ELEMENT_NODE:
print("标签名:", nd.tagName)
# print('节点名:', nd.nodeName)
for attr_ in nd.attributes.items():
print(attr_.name, attr_.value)
if nd.nodeType == xml.dom.Node.TEXT_NODE:
print('文本:', nd.wholeText)
if nd.nodeType == xml.dom.Node.ELEMENT_NODE and nd.hasChildNodes():
for nd_ in nd.childNodes:
list_nodes(nd_)
list_nodes(dom.documentElement)
[, ]
标签名: note
文本:
标签名: to
文本: Tove
文本:
标签名: from
文本: Jani
文本:
标签名: heading
文本: Reminder
文本:
标签名: body
文本: Don't forget me this weekend!
文本:
7. minicompat
提供了NodeList接口的定义。
builtins.list(builtins.object)
|- NodeList
builtins.tuple(builtins.object)
|- EmptyNodeList
这个接口就是所有子节点的返回类型。本质是list类型。
import xml.dom
import xml.dom.minidom
import xml.dom.expatbuilder
builder = xml.dom.expatbuilder.ExpatBuilder()
dom = builder.parseFile(open('note.xml', 'r'))
children = dom.childNodes
print(type(children))
8. xmlbuilder
实现了DOM level-3特征。主要如下几个类:
builtins.object
|- DOMBuilder:负责解析过程与处理。
|- DOMEntityResolver:负责解析DTD中的实体。
|- DOMInputSource:负责xml的数据输入。
这三个类相互协作解析xml数据。
下面是一个小例子,说明它们之间的协作关系。
import xml.dom
import xml.dom.xmlbuilder
builder = xml.dom.xmlbuilder.DOMBuilder()
input_stream = xml.dom.xmlbuilder.DOMInputSource()
# input_stream.encoding = 'utf-8'
# input_stream.systemId = 'http://mybatis.org/dtd/mybatis-3-config.dtd'
# input_stream.publicId = '-//mybatis.org//DTD Config 3.0//EN'
input_stream.byteStream = open('codes/books.xml', 'r')
result = builder.parse(input_stream)
print(result)
print(result.documentElement.nodeName)
books
9. pulldom
提供在SAX基础上的DOM处理模型。
|- builtins.object
|- DOMEventStream
|- ErrorHandler
|- xml.sax.handler.ContentHandler(builtins.object)
|- PullDOM
|- SAX2DOM
提供两个函数来封装DOMEventStream的处理细节。
|-parse(stream_or_string, parser=None, bufsize=None)
|
|- parseString(string, parser=None)
两个函数返回DOMEventStream对象,通过getEvent返回事件命名与Document对象。
import xml.dom
import xml.dom.pulldom
import xml.sax.expatreader
# 方式一:
result = xml.dom.pulldom.parse('codes/books.xml')
e, doc = result.getEvent()
print(e)
print(doc.documentElement)
# 方式二:
parser = xml.sax.expatreader.ExpatParser(namespaceHandling=True)
fd = open('codes/books.xml','r')
ds = xml.dom.pulldom.DOMEventStream(fd, parser=parser, bufsize=1024*10)
e_, doc_ = ds.getEvent()
print(e_)
START_DOCUMENT
START_DOCUMENT