xml解析

xml:可扩展标记语言,类似于HTML,用于传输或者描述数据

XML

应用

  • 在客户端(client)/服务器(server)之间传递数据
  • 用来保存有关系的数据
  • 用来做配置文件,在Android当中的布局文件和清单文件

语法

文档声明

  • 声明必须在第一行,最简单的声明
    <?xml version="1.0"?>
  • encoding属性用于指定编码格式
    <?xml version="1.0" encoding="utf-8"?>
  • standalone属性说明文档是否独立,即是否依赖于其他文档
    <?xml version="1.0" standalone="yes"?>

元素

  • xml文档中出现的标签,成对出现
  • 如果包含主体,则用以下形式
    <tag>content</tag>
  • 如果不包含主体,则可以使用如下形式
    <tag />
  • 标签中可以嵌套,不允许交叉嵌套
  • xml中必须且只能有一个根标签
  • xml中不会忽略主题中出现的空格和换行
  • 命名规范:区分大小写,不能以数字和_开头,不能以XML(Xml,xml等)开头,不能包含空格,名称中间不能有冒号
  • 属性:一个元素可以有多个属性,每个属性都有自己的名称和取值,用单引号或者双引号。e.g.<mytag name="value" .../>,属性不允许重复,也可用子标签来表示

注释

  • 格式<!--注释--\>

CDATA

  • 所有XML中的文本均会被解析器解析,只有CDATA区段中的文本会被解析器忽略。
  • 语法<![CDATA[ content ]]\>

特殊字符

特殊字符 替代符号
& &amp;
< &lt;
> &gt;
" &quot;
' &apos;

XML约束

  • 由于标签都是自定义的,若出现错误将无法正确读取xml文件信息。
  • 用来约束xml书写规范的文档称之为约束文档
    • 格式良好的xml文档:遵循XML语法的XML
    • 有效的XML:遵循约束文档的XML
  • 约束文档定义了XML中允许出现的元素名称,属性及元素出现的顺序等。

常用的约束技术

XML DTD

  • Document Type Definition(文档类型定义)
  • 约束XML的书写规范
  • dtd可以写在单独的文件中,扩展名为dtd,必须使用UTF-8编码(此时为外部),如果为内部文档则不一定要为UTF-8

    • 文件清单
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <?xml version="1.0">
      <!DOCTYPE 书架 SYSTEM "book.dtd">
      <书架>
      <书>
      <书名>第一本书书名</书名>
      <作者>第一本书作者</作者>
      <售价>第一本书售价</售价>
      </书>
      <书>
      <书名>第二本书书名</书名>
      <作者>第二本书作者</作者>
      <售价>第二本书售价</售价>
      </书>
      </书架>
  • 引入外部DTD文档

    • XML使用DOCTYPE语句指明它遵循的DTD文档有两种形式
      • 1.当引用的DTD文档在本地时,采用
        <!DOCTYPE 根元素 SYSTEM "DTD名称"\>
      • 2.当引用的DTD文档在公共网络上时,采用
        <!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档URL"\>
  • 在XML内部编写DTD
  • e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?xml version="1.0" encoding="GBK"?\>
    <!DOCTYPE 书架 [
    <!ELEMENT 书架 (书+)>
    <!ELEMENT 书(书名,作者,售价)>
    <!ELEMENT 书名(#PCDATA)>
    <!ELEMENT 作者(#PCDATA)>
    <!ELEMENT 售价(#PCDATA)>
    <!ATTLIST 书
    ISBN ID #REQUIRED
    COMMENT CDATA #IMPLIED
    出版社 CDATA "书名"
    <!ENTITY copyright "实体内容">
    ]>
  • DTD-定义元素

    • 在DTD文档中使用ELEMENT关键字来声明一个XML元素
    • 语法:<!ELEMENT 元素名称 使用规则>
      • 使用规则:
        • (#PCDATA):指示元素的主体内容只能是普通文本(parsed character Data)。
        • EMPTY:用于指示元素的主体内容。
        • ANY:用于指示元素的主题内容为任意类型。
        • (子元素):指示元素中包含的子元素。
      • 定义子元素及描述他们之间的关系
        • 如果子元素用逗号分开,说明必须按照声明的顺序去编写XML文档
        • 如果子元素用 “|“ 分开,说明任远其一
        • + * ? 来表示元素出现的次数
          • 如果子元素后面没有 + * ? 说明必须出现一次
          • + 表示至少出现一次
          • * 表示可有可无
          • ? 表示0次或1次
  • DTD-定义属性
    • 在DTD文档中使用ATTLIST关键字来为一个元素声明属性
    • 语法:<!ATTLIST 元素名称 属性1 属性值类型 设置说明···>
    • 属性值类型
      • CDATA:表明属性的取值为普通文本字符串
      • (a|b|c)说明只能从其中任选其一
      • ID 表示属性的取值不能重复
    • 设置说明
      • #REQUIRED:表示该属性必须出现
      • #IMPLIED:表示该属性可有可无
      • #FIXED:表示属性的取值为一个固定值 语法:#FIXED “固定值”
  • DTD-定义引用实体

    • 概念:在DTD中定义,在XML中使用
    • 语法:<!ENTITY 实体名称 “实体内容”>
    • 引用方式(主义实在XML中使用):&实体名称
    • e.g.
      1
      2
      3
      4
      DTD中定义:
      <!ENTITY copyright "cj5785">
      XML中引用
      &copyright
  • DTD-定义参数实体

    • 概念:在DTD中定义,在DTD中使用
    • 语法:<!ENTITY %实体名称 “实体内容”>
    • 引用方式(注意是在DTD中使用):%实体名称
    • e.g.
      1
      2
      3
      4
      5
      DTD中定义
      <!ENTITY %TAG_NAME "姓名|EMAIL|电话|地址">
      DTD中引用
      <!ENTITY 个人信息 (%TAG_NAME;|生日)>
      <!ENTITY 客户信息 (%TAG_NAME;|公司名)>

XML Schema

  • 也是一种用于定义和描述XML文档结构与内容的模式语言,其出现是为了克服DTD的缺陷
  • XML Schema VS DTD
    • XML Schema符合XML语法规范
    • DOM,SAX等XML API很容易解析出XML Schema文档中的内容
    • XML Schema对名称空间支持的非常好
    • XML Schema比XML DTD支持更多的数据类型,并支持用户自定义的数据类型
    • XML Schema定义约束的能力非常强大,可以对XML实例文档做出细致的语义限制
    • XML Schema不能像DTD一样定义实体,比DTD复杂,但XML Schema现在是W3C组织的标准,正在逐步取代DTD
  • 名称空间 namespace 对应一个约束文档
  • Schema约束

    • XML Schema文件自身就是一个XML文件,但它的拓展名通常为.xsd
    • 一个XML Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的XML称之为实例文档
    • 和XML文件一样,一个XML Schema文档也必须有一个根节点,但这个根节点的名字为schema
    • 编写了一个XML Schema约束文件后,通常需要把这个文件中声明的元素绑定到一个URI地址上,在XML Schema技术中有一个专业术语来描述这个过程,即把XML Schema文档声明中的元素绑定到一个名称空间上,以后XML文件可以通过这个URI(即名称空间)来告诉解析引擎,XML文件中编写的元素来自哪里,被谁约束
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://www.cj5785.com"
      elementFromDefault="qualified">
      <xs:element name='书架'>
      <xs:complexType>
      <xs:sequence maxOccurs='unbounded'>
      <xs:element name='书'>
      <xs:complexType>
      <xs:sequence>
      <xs:element name='书名' type='xs:string' />
      <xs:element name='作者' type='xs:string' />
      <xs:element name='售价' type='xs:string' />
      </xs:sequence>
      </xs:complexType>
      </xs:element>
      </xs:sequence>
      </xs:complexType>
      </xs:element>
      </xs:schema>
  • XDR(已不再使用)

  • SOX(已不再使用)

XML解析

解析的xml文档内容

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<书架>
<>
<书名>第一行代码(第二版)</书名>
<作者>郭霖</作者>
<售价>79.00元</售价>
</>
<>
<书名>自己动手写网络爬虫</书名>
<作者>罗刚</作者>
<售价>49.00元</售价>
</>
</书架>

Dom解析

  • DOM解析
    • 根据xml层级结构在内存中会分配一个树形结构
    • 在XML DOM每个元素都会被解析成一个节点Node,而常用的节点类型又分为:
      • 元素节点 Element
      • 属性节点 Attr
      • 文本节点 Text
      • 文档节点 Document
  • Dom解析xml的优缺点
    • 优点:因为分配了一个树形结构,很方便的实现了增加,修改和删除的操作。
    • 缺点:如果要解析的文件过大,一次性在内存中分配一个树形结构,造成内存的溢出。
  • DOM & SAX解析开发包
    • JAXP是sun公司推出的解析标准实现
    • Dom4j:开源组织推出的解析开发包
    • JAXP:(Java API for XML Processing)开发包是JavaSE的一部分,它由以下几个包及其子包组成:
      • org.w3c.dom:提供DOM方式解析XML的标准接口
      • org.xml.sax:提供SAX方式解析XML的标准接口
      • javax.xml:提供了解析XML文档的类
    • java.xml.parsers包中,定义了几个工厂类,用来获得DOM和SAX解析器对象
      • DocumentBuilderFactory
      • SAXParserFactory
  • 1.把整个文档先加载到内存中->Document所以可以对文档进行修改
  • 2.如果文件比较大,则可能导致内存溢出
  • 编程思路:DocumentBuilderFactory -> DocumentBuilder -> Document -> 通过节点获得NodeList -> 具体的Node
  • 如果想修改TransformerFactory -> Transformer -> transform(xmlSource , outputTarget)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public void domParse() throws Exception{
//获取DocumentBuilderFactory
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
//获取DocumentBuilder
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
//通过DocumentBuilder解析xml文档获取Document对象
Document document = documentBuilder.parse("book.xml");
//通过元素名称可以找到元素集合
NodeList nodeList = document.getElementsByTagName("售价");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
String connect = node.getTextContent();
System.out.println(connect);
}
//找到第二个元素
Node node = nodeList.item(1);
//读出相应节点的文本内容
String content = node.getTextContent();
System.out.println(content);
}

public void domModifyxml() throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
Document document = builder.parse("book.xml");
NodeList nodeList = document.getElementsByTagName("售价");
Node node = nodeList.item(1);
//修改节点的内容(此时只是在内存中生效)
node.setTextContent("99.00元");

//以下修改生效于xml
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
//数据源
Source xmlSource = new DOMSource(document);
//输出的目的地
Result outputTarget = new StreamResult("book.xml");
transformer.transform(xmlSource , outputTarget );
}

Sax解析

  • 解析方式:边读边解析
  • 当使用sax方式读到特定标签的时候,自动调用相应的方法进行操作。
  • sax解析的优缺点
    • 优点:不会造成内存的溢出
    • 缺点:不能修改xml文件
  • 解析的xml文档内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package domtest;

public class SaxParseTest {

public void saxParser() throws Exception {
//获取工厂
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = parserFactory.newSAXParser();
//获取XMLReader通过这个reader可以设置ContentHandler
XMLReader xmlReader = saxParser.getXMLReader();
//给xmlReader设置contentHandler,ContentHandler是一个接口,里面太多的方法没有实现
//不去直接实现ContentHandler而是继承它默认的实现DefaultHandler
xmlReader.setContentHandler(new Myhandler());
//解析xml文档
xmlReader.parse("book.xml");
}

private class Myhandler extends DefaultHandler{
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String text = new String(ch,start,length);
System.out.println("文本内容"+text);
}
@Override
public void endDocument() throws SAXException {
System.out.println("文档结束");
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("结束标签</"+qName+">");
}
@Override
public void startDocument() throws SAXException {
System.out.println("文档开始");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.println("开始标签<"+qName+">");
}
}

public void saxParserTest() throws Exception {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser parser = parserFactory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(new DefaultHandler() {
boolean isPrice = false;
int count = 0;
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (isPrice && count == 2) {
System.out.println(new String(ch,start,length));
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("售价".equals(qName)) {
isPrice = false;
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("售价".equals(qName)) {
isPrice = true;
count++;
}
}

});
reader.parse("book.xml");
}
}
  • book.java (java bean)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Book {
private String title;
private String auther;
private String price;

public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuther() {
return auther;
}
public void setAuther(String auther) {
this.auther = auther;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "Book [title=" + title + ", auther=" + auther + ", price=" + price + "]";
}
}
  • 用sax解析将其解析为集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void saxParse2list() throws Exception {
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
SAXParser parser = parserFactory.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(new DefaultHandler() {
ArrayList<Book> bookshelf = null;
Book book = null;
String tag = null;
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if ("书名".equals(tag)){
book.setTitle(new String(ch, start, length));
}else if("作者".equals(tag)){
book.setAuther(new String(ch, start, length));
}else if("售价".equals(tag)){
book.setPrice(new String(ch, start, length));
}
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//每次到结束标签都要将tag设置成null
tag = null;
if("书架".equals(qName)) {
for (Book book1 : bookshelf) {
System.out.println(book1);
}
}else if ("书".equals(qName)) {
//读到书的结束标签,把book对象添加到集合里
bookshelf.add(book);
}
}

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if ("书架".equals(qName)) {
//创建集合
bookshelf = new ArrayList<Book>();
}else if("书".equals(qName)){
//创建book对象
book = new Book();
}else if("书名".equals(qName)){
tag = "书名";
}else if("作者".equals(qName)){
tag = "作者";
}else if("售价".equals(qName)){
tag = "售价";
}
}

});
reader.parse("book.xml");
}

Pull解析

  • 首先导入pull的jar包(两个jar包kxml.jarxmlpull.jar),其原理其实就是sax解析。但pull解析可以在任意位置停止,而sax解析必须全部解析完成
  • 使用pull解析xml文件把xml中的内容放到集合中去
  • 步骤:
    • 1.创建解析器工厂
    • 2.根据解析器工厂创建解析器
    • 3.把要操作的文件放到解析器里面
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public void pullTest() throws Exception {
ArrayList<Book> books = null;
Book book = null;
//获取工厂
XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
//获取到xml解析器
XmlPullParser parser = parserFactory.newPullParser();
//给解析器设置一个输入源
//第一个参数输入源,第二个参数文档中用到的字符集编码
parser.setInput(new FileInputStream(new File("book.xml")),"utf-8");
//获取当前事件类型
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
//开始标签
//parser.getName()获取当前事件对应的元素名字
if ("书架".equals(parser.getName())) {
//创建一个集合
books = new ArrayList<>();
}else if ("书".equals(parser.getName())) {
//创建一个book对象
book = new Book();
}else if ("书名".equals(parser.getName())) {
//给book对象设置书名的属性
book.setTitle(parser.nextText());//parser.nextText()获取当前节点的下一个文本内容
}else if ("作者".equals(parser.getName())) {
//给book对象设置作者的属性
book.setAuther(parser.nextText());
}else if ("售价".equals(parser.getName())) {
//给book对象设置售价的属性
book.setPrice(parser.nextText());
}
break;
case XmlPullParser.END_TAG:
//结束标签
if ("书".equals(parser.getName())){
//把book对象添加到集合
books.add(book);
}
break;
}
//调用parse.next方法解析下一个元素 用这个结果来更新eventType,如果解析到文档结束则退出循环
//如果不更新eventType则就成为死循环了
eventType = parser.next();
}

//遍历集合
for (Book book2 : books) {
System.out.println(book2);
}
}
Donate comment here