08.XML处理之DOM编程

本主题主要说明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

你可能感兴趣的:(08.XML处理之DOM编程)