美文网首页
08.XML处理之DOM编程

08.XML处理之DOM编程

作者: 杨强AT南京 | 来源:发表于2019-02-02 19:53 被阅读37次

    本主题主要说明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'}
    {}
    <xml.dom.minidom.DOMImplementation object at 0x103fc7a58>
    

    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)
    
    
    <xml.dom.minidom.Document object at 0x103fd8468>
    

    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.dom.minidom.DOMImplementation object at 0x103fc7a58>
    
    # 下面是读取产生的xml文件
    with open('my.xml', 'r') as fd:
        print(fd.read())
    
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE HH
      PUBLIC '-//mybatis.org//DTD Config 3.0//EN'
      'http://mybatis.org/dtd/mybatis-3-config.dtd'>
        <HH>
            <myroot 属性="属性值">我是文本</myroot>
        </HH>
    

    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')
    
    
    <xml.dom.minidom.DOMImplementation object at 0x103fc7a58>
    

    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)
    
    
    [<xml.dom.minidom.DocumentType object at 0x103fe1bd0>, <DOM Element: note at 0x103f98178>]
    <DOM Element: note at 0x103f98178>
    标签名: 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))
    
    <class 'xml.dom.minicompat.NodeList'>
    

    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)
    
    
    <xml.dom.minidom.Document object at 0x104002d68>
    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
    <DOM Element: books at 0x104001a60>
    START_DOCUMENT
    

    相关文章

      网友评论

          本文标题:08.XML处理之DOM编程

          本文链接:https://www.haomeiwen.com/subject/akqesqtx.html