XML基础(一)

XML基础(一)

XML简介

XML:可扩展标记语言

用途:XML用来传输和存储数据(或用来结构化数据)

XML的标签没有被预定义。需要自己手动定义标签。

XML实例:

<?xml version="1.0" encoding="UTF-8"?>
<test>
	<item></item>
</test>

XML需要写头信息声明,其他包才能识别这是一个xml文件。

XML的树状结构以及语法

XML文档类似于HTML文档,都是使用标签进行描述。其中的标签是一种树形结构。

<root [prop=]>
	<child [prop=]>
    	<child-msg [prop=]></child-msg>
    </child>
</root>
  1. 对于HTML有些标签可以不是闭合标签,但是在xml中所有标签都必须是闭合标签!

  2. xml标签对于大小写是敏感的。<Letter></Letter> | <letter></letter> 这两者是不一样的!

  3. 属性必须加引号,像HTML中有些属性是可以直接写值的,但是xml中不可以。

  4. 在xml中一些特殊的标签符号必须使用实体进行书写。例如<p>1 < 2</p> 这样xml解析器会将小于号当成一个新的元素开始的标志。 所以必须使用实体表示,例如:<p>1 &lt 2</p>。这样的实体并不多!

    &it ;<小于
    &gt ;>大于
    &amp ;&and符
    &apos ;单引号
    &quot ;"双引号
  5. XML中空格会保留,不像HTML多个空格只留一个。

XML元素的命名规则

  • 名称可以包含数字,字母,其他字符
  • 名称不能以数字或标签符号开始
  • 名称不能以字母xml(XML、Xml、xML等等)开始
  • 名称不能包含空格

最好使用下划线命名,驼峰命名也是可以的

XML的可扩展性

下面的xml:

<root>
	<book>xxx</book>
    <name>xxx</name>
</root>

xml解析程序是可以获取到元素dom中的text的。那么我们进行扩展,xml解析器仍可以进行提取,因为是基于dom的,dom没有删除,那么就不会出错。 例如扩展成下面的样子:

<root>
	<book>xxx</book>
    <name>xxx</name>
    <id>xxx</id>
</root>
<root2>
	<id>xxx</id>
</root2>

XML的属性

xml属性通常提供不属于数据的信息,是dom元素的一些信息。例如:

<file type="docx">xxx</file>

正如之前说的,xml的属性必须加双引号。不管是字符串还是数值。

<file num="3">xxx</file>

在xml中应该避免书写属性。如果有一个信息既可以写成标签的属性,又可以写成标签的子元素,那么你尽量选择写成标签的子元素。这样方便与xml解析。

为什么要避免属性呢?

  • 属性不可以包含多个值
  • 属性不能包含树状结构
  • 属性不易扩展
  • 属性难以阅读与维护

一个原则是:元数据应该存储为数据,数据本身应该存储为元素!

XML的DTD

前面讲的只是为了将你的xml写成一个 “形式良好” 的xml。

通过 DTD(文档类型定义) 验证的xml才是 “合法” 的xml!

DTD的使用,例如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note SYSTEM "Note.dtd">
<root>
	<father>
        <name>xxx</name>
    	<chlid>
        	<name>xxx</name>
        </chlid>
    </father>
</root>

以上代码的 DOCTYPE 声明是对外部 DTD 文档的引用。

或直接引导文档里:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note
[
        <!ELEMENT root (father)>
        <!ELEMENT name (#PCDATA)>
        <!ELEMENT father (name, child)>
        <!ELEMENT child (name)>
]>
<root>
	<father>
        <name>xxx</name>
    	<chlid>
        	<name>xxx</name>
        </chlid>
    </father>
</root>
DTD
<!DOCTYPE note
[
        <!ELEMENT root (father)>
        <!ELEMENT name (#PCDATA)>
        <!ELEMENT father (name, child)>
        <!ELEMENT child (name)>
]>

DTD还有一个替代者:XML Schema

DTD

DTD定义XML的书写标准,也可以看做XML的格式标准。

例如上面给的DTD代码:

<!DOCTYPE note
[
        <!ELEMENT root (father)>
        <!ELEMENT name (#PCDATA)>
        <!ELEMENT father (name, child)>
        <!ELEMENT child (name)>
]>
  • !DOCTYPE note:定义此文档是note类型的文档
  • !ELEMENT rootroot元素有一个子元素father
  • !ELEMENT namename元素是**#PCDATA**类型的

PCDATA与CDATA

PCDATA

PCDATA:被解析的字符数据(parsed character data),字符数据就是XML元素开始标签与结束标签之间的文本。

PCDATA 是会被解析器解析的文本,这些文本将被解析器检查实体以及标签。(文本中的标签会被当做标记处理,实体会展开成相应的东西)

CDATA

CDATA:字符数据(character data)

CDATA 是不会被解析的文本,这些文本中不会被解析器检查实体以及标签

DTD声明元素

DTD文档中也是通过元素来进行声明的。它声明的是xml文档中的元素

  • <!ELEMENT element-name category>
  • <!ELEMENT element-name (element-content)>

在声明xml元素的时候,一个元素可以对应多个规则,例如:

<!ELEMENT father (#PCDATA | name | child)>

说明 father 标签中可以包含:PCDATA文本,name元素,child元素。但是只能出现一次。

声明空元素

空元素对应的XML元素就是单闭合标签,其中不能包含文本,所以叫空

<!ELEMENT element-name EMPTY>
<!--例如-->
<!ELEMENT xyz EMPTY>
<xyz/>
声明只有 PCDATA文本 的元素

PCDATA元素对应的XML元素就是包含文本内容的双闭合标签,其中包含PCDATA文本。

<!ELEMENT element-name (#PCDATA)>
<!--例如:-->
<!ELEMENT name (#PCDATA)>
<name>xxxx &amp; xxxx</name>
声明带有子元素的元素

带有子元素的元素对应的XML就是包含子标签的双闭合标签。

<!ELEMENT element-name (child1, child2..., childN)>
<!--例如:-->
<!ELEMENT father (name, child)>
<father>
    <name>xxx</name>
    <chlid>
        <name>xxx</name>
    </chlid>
</father>
声明带有任何内容的元素

ANY元素对应的XML元素就是包含任意内容(标签,文本)的双闭合标签可以包含任何可解析数据的组合。

<!ELEMENT element-name ANY>
<!--例如:-->
<!ELEMENT root ANY>
<root>
	<father>
        <name>xxx</name>
    	<chlid>
        	<name>xxx</name>
        </chlid>
    </father>
</root>
声明只出现一次的元素

声明只出现一次的元素对应的XML元素就是只在声明的元素内出现且只出现一次!

<!ELEMENT element-name (child-name)>
<!--例如:-->
<!ELEMENT root (father)>
<root>
	<father>
        <name>xxx</name>
    	<chlid>
        	<name>xxx</name>
        </chlid>
    </father>
</root>

father只在root中出现,且只出现一次。

声明至少出现一次的元素

声明至少出现一次的元素对应的XML元素就是只在声明的元素内出现,且至少出现一次。

<!ELEMENT element-name (child-name+)>
<!--例如:-->
<!ELEMENT root (father+)>
<root>
	<father>
        <name>xxx</name>
    	<chlid>
        	<name>xxx</name>
        </chlid>
    </father>
    <father>
    	<name>xxx</name>
    </father>
</root>
声明出现零次或多次的元素
<!ELEMENT element-name (child-name*)>
<!--例如:-->
<!ELEMENT root (father*)>
声明出现零次或一次的元素
<!ELEMENT element-name (child-name?)>
<!--例如:-->
<!ELEMENT root (father?)>

DTD声明属性

属性的声明使用以下的语法:

<!ATTLIST element-name arrtibute-name attribute-type arrtibute-value>
<!--attribute-value也可以叫做默认值-->
<!--例如:-->
<!ATTLIST payment type CDATA "check">
<payment type="check">
类型描述
CDATA值为字符数据 (character data)
(en1|en2|…)此值是枚举列表中的一个值
ID值为唯一的 id
IDREF值为另外一个元素的 id
IDREFS值为其他 id 的列表
NMTOKEN值为合法的 XML 名称
NMTOKENS值为合法的 XML 名称的列表
ENTITY值是一个实体
ENTITIES值是一个实体列表
NOTATION此值是符号的名称
xml:值是一个预定义的 XML 值

默认属性值可使用下列值 :

解释
属性的默认值
#REQUIRED属性值是必需的
#IMPLIED属性不是必需的
#FIXED value属性值是固定的

python解析XML

python有三种方式解析XML:SAX、DOM、ElementTree

  1. SAX(Simple API for XML)

    python标准库包含 SAX 解析器,SAX使用事件驱动,通过在解析XML过程中出发事件并调用回调函数处理XML

  2. DOM(Document Object Model)

    将XML数据在内存中解析成一个树,通过对树的操作来操作XML

  3. ElementTree(元素树)

    ElementTree是一个轻量级的DOM,速度快,内存消耗少

SAX速度 > ElementTree速度 > DOM速度。但是SAX需要用户自己写回调函数进行处理!

例题XML:

<collection shelf="New Arrivals">
<movie title="Enemy Behind">
   <type>War, Thriller</type>
   <format>DVD</format>
   <year>2003</year>
   <rating>PG</rating>
   <stars>10</stars>
   <description>Talk about a US-Japan war</description>
</movie>
<movie title="Transformers">
   <type>Anime, Science Fiction</type>
   <format>DVD</format>
   <year>1989</year>
   <rating>R</rating>
   <stars>8</stars>
   <description>A schientific fiction</description>
</movie>
   <movie title="Trigun">
   <type>Anime, Action</type>
   <format>DVD</format>
   <episodes>4</episodes>
   <rating>PG</rating>
   <stars>10</stars>
   <description>Vash the Stampede!</description>
</movie>
<movie title="Ishtar">
   <type>Comedy</type>
   <format>VHS</format>
   <rating>PG</rating>
   <stars>2</stars>
   <description>Viewable boredom</description>
</movie>
</collection>

SAX解析XML

SAX是事件驱动的,那么就涉及到了两部分:解析器(parser)和事件处理器(handler)

handler用于定义遇到某标签时的回调函数,然后将事件处理器放到parser中代理,解析器解析xml文件时内部自动调用事件处理器!

ContentHandler

事件处理器必须继承自:xml.sax.handler.ContentHandler

其中有几个方法(这几个方法是回调函数)

  • def characters(content)方法

    调用时机:

    • 从行开始,到遇到标签之前,存在字符,content就是这些字符
    • 从一个标签开始,到下一个标签之前,存在字符,content就是这些字符
    • 从一个标签开始,到遇到行结束符之前,存在字符,content就是这些字符
    • 标签可以是开始标签也可以是结束标签。
  • def startDocument()方法

    文档启动时调用

  • def endDocument()方法

    文档结束时调用

  • def startElement(name, attrs)方法

    遇到XML开始标签时调用,name是标签名,attrs是标签的属性

  • def endElement(name)方法

    遇到XML结束标签时调用

parser

parser的一些方法来自xml.sax

其中有以下几个函数

  • xml.sax.make_parser([parser_list])

    创建一个新的解析器

    • parser_list:可选参数,解析器列表(选中参数的话,就是根据解析器创建一个新的解析器)
  • xml.sax.parse(xmlfile, contenthandler [, errorhandler])

    解析一个xml文件

    • xmlfile:xml文件
    • contenthandler:事件处理器,必须是一个ContentHandler的实例
    • errorhandler:如果指定该参数,它必须也是一个ContentHandler的实例
  • xml.sax.parserString(xmlstring, contenthandler [, errorhandler])

    解析一个xml字符串

    • xmlstring:xm字符串
    • contenthandler:事件处理器,必须是一个ContentHandler的实例
    • errorhandler:如果指定该参数,它必须也是一个ContentHandler的实例

例如:这样一个解析过程

在这里我使用了一个小算法,handler内部遍历节点的过程是后序遍历树。所以我使得每个节点的num+1,如果是叶子节点,则减下来是0,加入字典。如果不是则减下来不是0。

from xml.sax.handler import ContentHandler
from xml.sax import make_parser
from xml.sax import parse

class MyHandler(ContentHandler):
    def __init__(self):
        super(MyHandler, self).__init__()
        self.current_dom = ''
        self.content = ''
        self.dic = {}
        self.stack = []
        self.num = 0
    
    def startElement(self, tag, attributes):
        self.current_dom = tag
        self.num += 1
        self.stack.append(self.num)
        
    def characters(self, content):
        self.content = content
        
    def endElement(self, tag):
        num = self.stack.pop()
        if (self.num - num) == 0:
            if tag in self.dic:
                self.dic[tag].append(self.content)
            else:
                self.dic[tag] = [self.content]
        print(self.content)
        
handler = MyHandler()
parser = make_parser()
parser.setContentHandler(handler)
parser.parse("test.xml")
print(handler.dic)
War, Thriller
DVD
2003
PG
10
Talk about a US-Japan war


Anime, Science Fiction
DVD
1989
R
8
A schientific fiction


Anime, Action
DVD
4
PG
10
Vash the Stampede!


Comedy
VHS
PG
2
Viewable boredom




{'type': ['War, Thriller', 'Anime, Science Fiction', 'Anime, Action', 'Comedy'], 'format': ['DVD', 'DVD', 'DVD', 'VHS'], 'year': ['2003', '1989'], 'rating': ['PG', 'R', 'PG', 'PG'], 'stars': ['10', '8', '10', '2'], 'description': ['Talk about a US-Japan war', 'A schientific fiction', 'Vash the Stampede!', 'Viewable boredom'], 'episodes': ['4']}

DOM解析XML

from xml.dom.minidom import parse
import xml.dom.minidom

DOMTree = xml.dom.minidom.parse("movies.xml")
collection = DOMTree.documentElement
if collection.hasAttribute("shelf"):
   print "Root element : %s" % collection.getAttribute("shelf")

movies = collection.getElementsByTagName("movie")

可以看出,它向js中的获取DOM节点类似。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值