XML 基础
XML语言
一、XML是什么?作用是什么?
l XML ( eXtensible Markup Language )语言是一种可扩展的标记语言。其中的可扩展是相对HTML来说的。因为XML标签没有被预定义,需要用户自行定义标签。
l XML 被设计的宗旨是:是表示数据,而非显示数据。
作用:
l Java开发中,传统的配置文件是*.properties属性文件(key=value),而XML表示的数据更为丰富。
l XML技术除用于描述有关系的数据外,还经常用作软件配置文件,以描述程序模块之间的关系。如:
这样的关系数据该如何处理?
用XML语言处理:
总结:在XML语言中,它允许用户自定义标签,一个标签用于描述一段数据,一个标签分为开始标签和结束标签,在这两者之间又可以使用其它标签描述其他数据,以此来实现数据关系的描述。
二、XML的基本语法
1、文档声明
文档的声明必须出现在第一行,之前连空行和注释都不能有.
文档声明, 最简单的语法: <?xml version="1.0"?>
encoding="UTF-8", 如果不添加,默认的采用的是UTF-8编码,保存在磁盘上的编码要与声明的编码一样!
standalone属性,用来说明文档是否独立,即文档是否依赖其他文档。
<?xml version="1.0" encoding="utf-8"?>
2、元素
1) xml 元素是指xml中的标签。一个标签分为开始标签和结束标签:
a) 包含标签主体:<mytag>some content</mytag>
b) 不含标签主体:<mytag/>
2) 一个XML文档必须有且仅有一个根标签,其他标签都是这个根标签的子标签或孙标签。
3) 一个标签中可以嵌套若干子标签,但所有标签必须合理的嵌套,不允许有交叉嵌套。
4) xml 中的空格与换行不会被忽略,会当做原始内容被处理.
5) 一个XML元素可以包含字母、数字以及其它一些可见字符,但必须遵守下面的一些规范:
a) 区分大小写,例如,<P>和<p>是两个不同的标记。
b) 不能以数字或"_" (下划线)开头。
c) 不能以xml(或XML、或Xml 等)开头。
d) 不能包含空格。
e) 名称中间不能包含冒号(:) (有特殊用途)。
3、属性
1) 一个标签可以有多个属性,每个属性都有它自己的名称和取值,例如: <mytag name="value"/>
2) 属性值一定要用双引号(")或单引号(')引起来
3) 定义属性必须遵循与标签相同的命名规范
4) 在XML技术中,标签属性所代表的信息,也可以被改成用子元素的形式来描述。
4.注释
注释的语法: <!--这是注释哦.-->
注释不能写在第一行.
注释不能够嵌套.
1 <?xml version="1.0" encoding="utf-8"?> 2 <!-- 注释 --> 3 <书架> 4 <书 出版社="中国上海"> 5 <名字>诛仙</名字> 6 <作者>萧鼎</作者> 7 <价格>32.00</价格> 8 <出版日期>2007年</出版日期> 9 </书> 10 <书 出版社="中国北京"> 11 <名字>笑傲江湖</名字> 12 <作者>金庸</作者> 13 <价格>50.00</价格> 14 </书> 15 </书架>
5.CDATA区
用于将一段内容当做普通文本.
语法:<![CDATA[
文本内容
]]>
6.特殊字符
& & ampersand
< < less than
> > great than
" &;quot; quotation
' ' apostrophe
7.处理指令(processing instruction) (PI)
作用:用来指挥软件如何解析XML文档
语法: <?xml ?>
如:常用的PI指令:
XML声明:<?xml version=“1.0” encoding=“GB2312”?>
xml-stylesheet指令:<?xml-stylesheet type = “text/css” herf=”test.css”>
<?xml version="1.0" encoding="UTF-8"?> <!-- 这里用处理命令加入css样式 --> <?xml-stylesheet type="text/css" href = "PITest.css" ?> <中国> <北京>北京</北京> <上海>上海</上海> <深圳>深圳</深圳> </中国>
PITest.css 文档如下:
1 @CHARSET "UTF-8"; 2 北京{ 3 font-size: 100px ; 4 color: red; 5 } 6 上海{ 7 font-size: 110px ; 8 color: green ; 9 } 10 深圳{ 11 font-size:100px ; 12 color: yellow ; 13 }
三、XML的约束
1、在XML 技术中可以编写一个文档来约束XML 文档里面的书写规范,这称为XML约束。
2、XML 约束技术:
常用的有:XML DTD 和 XML Schema
3、XML 约束的必要性:
a) XML都是用户自定义的标签,若出现小小的错误,软件程序将不能正确地获取文件中的内容而报错.
总结:
格式良好的XML 文档,遵循语法规则的XML 文档。
有效的XML 文档,遵循约束文档的 XML 文档。
约束文档定义了在XML中允许出现的元素名称、属性及元素出现的顺序等等。
四、DTD的基本语法
1.DTD 约束的两种方式:
DTD 约束可以作为一个单独的文档编写,也可以编写在XML 文档内。(编写XML内部的DTD代码),当作为单独文件时,要用utf-8格式存储。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 在XML 写入DTD 约束 --> 3 <!DOCTYPE 世界[ 4 <!ELEMENT 世界 (国家+) > 5 <!ELEMENT 国家 (名字)> 6 <!ELEMENT 名字 (#PCDATA) > 7 <!ATTLIST 名字 8 所属洲 CDATA #IMPLIED 9 > 10 <!ATTLIST 国家 所属洲 (亚洲|欧洲|南美洲|南极洲|澳洲|非洲|北美洲) "亚洲"> 11 ]> 12 13 <世界> 14 <国家 所属洲 = "亚洲"> 15 <名字>中国</名字> 16 </国家> 17 <国家> 18 <名字 所属洲 = "美洲">美国</名字> 19 </国家> 20 </世界>
XML 引用 DTD 约束文件时:
1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE 书架 SYSTEM "book.dtd" > 3 <书架> 4 <书> 5 <名字>诛仙</名字> 6 <作者>萧鼎</作者> 7 <价格>32.00</价格> 8 </书> 9 <书 > 10 <名字>笑傲江湖</名字> 11 <作者>金庸</作者> 12 <价格>50.00</价格> 13 </书> 14 </书架>
a)本地的 : <!DOCTYPE 根元素 SYSTEM “DTD文档路径”>
b)互联网上的: <!DOCTYPE 根元素 PUBLIC “DTD文档路径” "dtd的URL地址">
如:<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
注意book.dtd文档如下:
<!ELEMENT 书架 (书+)> <!ELEMENT 书 (名字,作者,价格)> <!ELEMENT 名字 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 价格 (#PCDATA)>
DTD 编写细节:
1、 元素定义
在DTD 文件中用ELEMENT 声明一个 XML元素,语法:
<!ELEMENT 元素名称 元素类型>
元素类型可以是元素内容。
元素内容,则需要用() 括起来,
<!ELEMENT 世界 (国家+) >
<!ELEMENT 国家 (名字,所属洲)>
<!ELEMENT 名字 (#PCDATA)>
元素类型的组成遵循正则表达式的格式:
1、用逗号分隔,表示内容的出现顺序必须与声明时一致。
<!ELEMENT MYFILE (TITLE,AUTHOR,EMAIL)>
2、用|分隔,表示任选其一,即多个只能出现一个
<!ELEMENT MYFILE (TITLE|AUTHOR|EMAIL)>
3、在元素内容中也可以使用+、*、?等符号表示元素出现的次数:
+: 一次或多次 (书+) regex
?: 0次或一次 (书?)
*: 0次或多次 (书*)
4、也可使用圆括号( )批量设置,例
<!ELEMENT FILE ((TITLE*, AUTHOR?, EMAIL)* | COMMENT)>
元素类型,则直接书写,DTD规范定义了如下几种类型:
EMPTY:用于定义空元素,例如<br/> <hr/>
ANY:表示元素内容为任意类型。
2、 属性定义
xml文档中的标签属性需通过ATTLIST为其设置属性
语法格式:
<!ATTLIST 元素名
属性名1 属性值类型 设置说明
属性名2 属性值类型 设置说明
……
>
设置说明:
#REQUIRED:必须设置该属性
#IMPLIED:可以设置也可以不设置
#FIXED:说明该属性的取值固定为一个值,在 XML 文件中不能为该属性设置其它值。但需要为该属性提供这个值
直接使用默认值:在 XML 中可以设置该值也可以不设置该属性值。若没设置则使用默认值。但需要为该属性提供这个值
常用属性值类型
l CDATA:表示属性值为普通文本字符串。
l ENUMERATED
l ID indentity
l ENTITY(实体)
1 <!ELEMENT 国家 (名字)> 2 <!ATTLIST 国家 3 所属洲 CDATA #REQUEIRED >
3、 实体定义
实体用于为一段内容创建一个别名,以后在XML 文档中就可以使用别名引用这段内容。
实体可分为两种类型:引用实体和参数实体。
引用实体主要在XML 文档中被使用:
语法格式:<!ENTITY 实体名称 “实体内容” >: 直接转变成实体内容。
引用方式:&实体名称;
参数实体则是在DTD 文档中自身使用:
语法格式:<!ELEMENT % 实体名称 “实体内容” >
引用方式 :%实体名称
<!ENTITY % TAG_NAMES "姓名 | EMAIL | 电话 | 地址"> <!ELEMENT 个人信息 (% TAG_NAMES; | 生日)> <!ELEMENT 客户信息 (% TAG_NAMES; | 公司名)>
五、JUnit 测试类
JUnit 作为测试工具,可以通过注释的方法来代替写main方法,同时来测试相应的方法:再用JUbit 之前 先导入junit.jar 包。
@Test :用来注释需要被测试的方法。
@Before : 用来注释在运行@Test方法之前需要被运行的方法。 注意:如果有多个方法被@Before 注释,则从最后一个开始运行。
@After : 则注释在运行@Test方法之后需要被运行的方法。 注意:如果有多个方法被@After 注释,则从第一个开始运行。
@AfterClass :注释的方法在类释放时运行。
@BeforeClass : 注释的方法在类加载时运行。
1 import org.junit.After; 2 import org.junit.AfterClass; 3 import org.junit.Before; 4 import org.junit.BeforeClass; 5 import org.junit.Test; 6 7 8 public class JUnitDemo { 9 @Test 10 public void test_1() { 11 System.out.println("Hello JUnit!"); 12 } 13 @Before 14 public void before(){ 15 System.out.println("Before"); 16 } 17 @AfterClass 18 public static void afterClass(){ 19 System.out.println("AfterClass"); 20 } 21 @After 22 public void after(){ 23 System.out.println("After"); 24 } 25 @BeforeClass 26 public static void beforeClass(){ 27 System.out.println("BeforeClass"); 28 } 29 }
六、JAXP进行DOM解析
XML 的两种解析方式:DOM 解析和SAX 解析。
DOM (Document Object Model ,文档对象模式 ) 解析,原理DOM解析器在解析XML文档时,会把文档中的所有元素(document\element\attribute\character),按照其出现的层次关系,解析成一个个Node对象(节点)
在dom中,节点之间关系如下:
1、 位于一个节点之上的节点是该节点的父节点(parent)
2、 一个节点之下的节点是该节点的子节点(children)
3、 同一层次,具有相同父节点的节点是兄弟节点(sibling)
4、 一个节点的下一个层次的节点集合是节点后代(descendant)
5、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)
Node对象提供了一系列常量来代表结点的类型,当开发人员获得某个Node类型后, 就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法。
Node对象提供了相应的方法去获得它的父结点或子结点。编程人员通过这些方法就可以读取整个XML文档的内容、或添加、修改、删除XML文档的内容了。
练习:
1.读取节点的文本内容
2.读取属性值
3.添加节点
4.删除节点
5.更新节点
6.打印所有元素节点的名称.
1 package cn.itheima.xml.day01; 2 3 import javax.xml.parsers.DocumentBuilderFactory; 4 import javax.xml.transform.TransformerFactory; 5 import javax.xml.transform.dom.DOMSource; 6 import javax.xml.transform.stream.StreamResult; 7 8 import org.junit.After; 9 import org.junit.Before; 10 import org.junit.Test; 11 import org.w3c.dom.Document; 12 import org.w3c.dom.Element; 13 import org.w3c.dom.NamedNodeMap; 14 import org.w3c.dom.Node; 15 import org.w3c.dom.NodeList; 16 17 public class DOMTest { 18 /* 19 1.读取节点的文本内容 20 2.读取属性值 21 3.添加节点 22 4.删除节点 23 5.更新节点 24 6.打印所有元素节点的名称. 25 */ 26 /* 27 * DOM解析器在解析XML文档时,会把文档中的所有元素(document\element\attribute\character),按照其出现的层次关系, 28 * 解析成一个个Node对象(节点)。 29 * 30 * Node对象提供了一系列常量来代表结点的类型,当开发人员获得某个Node类型后, 31 * 就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法。 32 */ 33 34 //1、读取节点的文本内容 35 private Document doc = null ; 36 @Test 37 public void readContent() { 38 //获得标签名为"名字" 的 NodeList ; 39 NodeList lists = doc.getElementsByTagName("名字") ; 40 //获得节点的文本类容 41 for (int i = 0; i < lists.getLength(); i++) { 42 System.out.println(lists.item(i).getTextContent()); 43 } 44 } 45 //2、读取属性值 46 @Test 47 public void getAttr() { 48 //获取元素“书”的NodeList 49 NodeList lists = doc.getElementsByTagName("书") ; 50 //通过遍历lists ,获取每个节点中的属性值 51 for (int i = 0; i < lists.getLength(); i++) { 52 NamedNodeMap attributes = lists.item(i).getAttributes(); 53 for (int j = 0; j < attributes.getLength(); j++) { 54 System.out.println(attributes.item(j).getTextContent()); 55 } 56 } 57 } 58 //3.添加节点 59 //在“书”标签下添加一个<出版日期>的元素 60 @Test 61 public void addNode() throws Exception{ 62 //创建一个<出版日期> 添加到内存中 63 Element element = doc.createElement("出版日期") ; 64 //设置该标签的的文本值 65 element.setTextContent("2007年"); 66 //通过Node 类中的 appendChild 方法将<出版日期>添加到节点的子节点列表的末尾 67 NodeList lists = doc.getElementsByTagName("书"); 68 69 //问题:为什么只在第二个"书" 标签添加了该子节点。 70 //而且如果存在<出版日期> 子节点,为什么添加之后有两个该节点。 71 /* 72 Node appendChild(Node newChild) throws DOMException 73 将节点 newChild 添加到此节点的子节点列表的末尾。如果 newChild 已经存在于树中,则首先移除它。 74 */ 75 for (int i = 0 ; i < lists.getLength() ; i ++) { 76 System.out.println(lists.item(i).getNodeName()); 77 lists.item(i).appendChild(element) ; 78 } 79 updateXML() ; 80 } 81 82 //如何将修改后的DOC写到XML中? 83 public void updateXML() throws Exception { 84 /* 85 * javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换为某种格式后进行输出 86 * Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地。我们可以通过: 87 * > javax.xml.transform.dom.DOMSource 类来关联要转换的document对象, 88 * > javax.xml.transform.stream.StreamResult 对象来表示数据的目的地。 89 */ 90 91 //创建TransformFactory 对象: 92 TransformerFactory.newInstance() 93 .newTransformer() 94 .transform(new DOMSource(doc), new StreamResult("src/cn/itheima/xml/day01/book.xml")); 95 } 96 97 //4.删除节点 98 //删除“出版日期”节点。 99 @Test 100 public void removeNode() throws Exception { 101 //获取 "出版日期" 的节点 102 Node node = doc.getElementsByTagName("出版日期").item(0) ; 103 104 //获取 node 节点的父节点。 105 //通过父节点删除"出版日期"节点 106 node.getParentNode().removeChild(node) ; 107 updateXML() ; 108 } 109 110 //5.更新节点 111 //将 "笑傲江湖" 的“价格”修改为 50.00 112 /* 113 * 1、获取“名字”的NodeList , 然后遍历 值为“笑傲江湖” 的节点。 114 * 2、获取“笑傲江湖”节点的兄弟节点。 115 */ 116 @Test 117 public void updateNode() throws Exception{ 118 //获取“笑傲江湖”的节点 119 NodeList nodeList = doc.getElementsByTagName("名字"); 120 for (int i = 0; i < nodeList.getLength(); i++) { 121 // System.out.println(nodeList.item(i).getTextContent()); 122 if ( nodeList.item(i).getTextContent().equals("笑傲江湖")) { 123 NodeList childNodes = nodeList.item(i).getParentNode().getChildNodes(); 124 for (int j = 0; j < childNodes.getLength(); j++) { 125 if (childNodes.item(j).getNodeName().equals("价格")) { 126 childNodes.item(j).setTextContent("50.00") ; 127 break ; 128 } 129 } 130 } 131 } 132 //通过获取 其父节点然后通过父节点获取到“价格”节点,修改其节点的值 133 /*NodeList lists = node.getParentNode().getChildNodes() ; 134 for(int i = 0 ; i < lists.getLength() ; i++) { 135 if( lists.item(i).getNodeName().equals("价格") ) { 136 lists.item(i).setTextContent("50.00"); 137 break ; 138 } 139 }*/ 140 updateXML() ; 141 } 142 //6.打印所有元素节点的名称. 143 @Test 144 public void printNode(){ 145 treeWeek(doc) ; 146 } 147 public void treeWeek(Node node) { 148 if(Node.ELEMENT_NODE == node.getNodeType()){ 149 System.out.println(node.getNodeName()); 150 } 151 NodeList nl = node.getChildNodes(); 152 for (int i = 0; i < nl.getLength(); i++) { 153 Node item = nl.item(i); 154 treeWeek(item); 155 } 156 } 157 @Before 158 public void getDOM() throws Exception{ 159 /* 160 * 1、获得DocumentBuilderFactory 对象。 161 * 2、通过DocumentBuilderFactory 对象创建 DocumentBuilder 对象(DOM 解析器对象)。 162 * 3、通过DocumentBuilder 对象解析XML文件,进而可以利用DOM特性对整个XML文档进行操作了。 163 */ 164 doc = DocumentBuilderFactory.newInstance() 165 .newDocumentBuilder() 166 .parse("src/cn/itheima/xml/day01/book.xml") ; 167 } 168 @After 169 public void setDOM() { 170 doc = null ; 171 } 172 }
总结:
DOM 解析 的优点是增删改方便,
缺点,如果要解析的XML 文档过大,就会导致内存溢出(Out Of Memory , OOM),因为DOM解析需要将XML 文档内容全部加载到内存中再解析。
七、JAXP进行SAX解析
l SAX (Simple API for XML ) 解析:SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档装载完才对文档进行操作。
l SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器:
l 解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
l 解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
l 事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。
SAX解析步骤:
1、使用SAXParserFactory创建SAX解析工厂
2、通过SAX解析工厂得到解析器对象
3、通过解析器对象得到一个XML的读取器
4、设置读取器的事件处理器
5、解析xml文件
package cn.itheima.xml.day01; import java.io.IOException; import javax.xml.parsers.SAXParserFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /* * SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档装载完才对文档进行操作。 * * SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器: * 解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。 * * 解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分(startDocument,startElement,character,endElement,endDocument), * 都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时, * 会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。 * * 事件处理器由程序员编写,程序员通过事件处理器中方法的参数, * 就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。 * javax.xml.parsers.SAXParserFactory * */ /* SAX 解析练习. 1.读取节点的文本内容 2.读取属性值 3.添加节点 4.删除节点 5.更新节点 6.打印所有元素节点的名称. 思路: 1、使用SAXParserFactory创建SAX解析工厂 2、通过SAX解析工厂得到解析器对象 3、通过解析器对象得到一个XML的读取器 4、设置读取器的事件处理器 5、解析xml文件 */ public class SAXTest { private XMLReader reader = null ; //打印出解析出的XML所有内容 @Test public void printTest() throws IOException, SAXException { //设置读取器的事件处理器: reader.setContentHandler(new MyHandler()) ; reader.parse("src/cn/itheima/xml/day01/book.xml") ; } //1.读取节点的文本内容 /* * 读取节点名字为“名字”的文本内容 */ @Test public void getTextContent() throws IOException, SAXException { reader.setContentHandler(new getTestContent()) ; reader.parse("src/cn/itheima/xml/day01/book.xml") ; } //2.读取属性值 /* * 读取节点名字为"书" 的属性值 */ @Test public void getAttributeName() throws IOException, SAXException { reader.setContentHandler(new getAttribute()) ; reader.parse("src/cn/itheima/xml/day01/book.xml") ; } // 在测试之前获得一个XML读取器: @Before public void getReader() throws Exception { /* 1、使用SAXParserFactory创建SAX解析工厂 2、通过SAX解析工厂得到解析器对象 3、通过解析器对象得到一个XML的读取器 */ reader = SAXParserFactory.newInstance() .newSAXParser() .getXMLReader() ; } //测试完重置reader = null @After public void setReader() { reader = null ; } } class getAttribute extends DefaultHandler { @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("书".equals(qName)) { for (int i = 0; i < attributes.getLength(); i++) { System.out.println(attributes.getValue(i)); } } } } class getTestContent extends DefaultHandler{ private boolean flag = false ; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("名字".equals(qName)) { flag = true ; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (flag){ System.out.println("文本类容:"+new String(ch,start,length)); flag = false ; } } } //编写事件处理器: /* 继承org.xml.sax.helpers.DefaultHandler * DefaultHandler 类: * SAX2 事件处理程序的默认基类。应用程序编写者可以在他们仅需实现部分接口时扩展此类; * 类似于ContentHandler 接口的适配器(adapter) ; */ class MyHandler extends DefaultHandler { //在此类中重写我们需要的几个方法: @Override public void startDocument() throws SAXException { System.out.println("XML文件开始解析:"); } @Override public void endDocument() throws SAXException { System.out.println("XML文件解析结束:"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("元素标签开始:"+ qName); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("元素标签结束:" + qName); } @Override public void characters(char[] ch, int start, int length) throws SAXException { System.out.println("文本类容:"+new String(ch,start,length)); } }
总结:优点查找非常快,但是没DOM 解析 方法那样直观明白。
八、Dom4J简介、进行解析
Dom4J 则是一层一层的解析XML文件,而且直观。
1 package cn.itheima.xml.day01; 2 3 import java.io.FileNotFoundException; 4 import java.io.FileOutputStream; 5 import java.io.UnsupportedEncodingException; 6 import java.util.Iterator; 7 import java.util.List; 8 9 import org.dom4j.Attribute; 10 import org.dom4j.Document; 11 import org.dom4j.DocumentException; 12 import org.dom4j.Element; 13 import org.dom4j.io.OutputFormat; 14 import org.dom4j.io.SAXReader; 15 import org.dom4j.io.XMLWriter; 16 import org.junit.After; 17 import org.junit.Before; 18 import org.junit.Test; 19 20 /* 21 * Dom4j是一个非常优秀的Java XML API,具有性能优异、功能强大和极易使用的特点。 22 */ 23 public class DOM4JTest { 24 /* 25 1.读取节点的文本内容 26 2.读取属性值 27 3.添加节点 28 4.删除节点 29 5.更新节点 30 6.打印所有元素节点的名称. 31 */ 32 private Document doc = null ; 33 //1.读取节点的文本内容 34 /* 35 * 读取"书"节点下,“名字”节点的文本内容 36 * DOM4J 思路: 37 * 1.获取文档的根节点. 38 Element root = document.getRootElement(); 39 40 2.取得某个节点的子节点. 41 Element element=node.element(“书名"); 42 43 3.取得节点的文字 44 String text=node.getText(); 45 46 */ 47 @Test 48 public void getNodeText() { 49 //获得跟节点 50 Element root = doc.getRootElement() ; 51 //通过跟节点获取到子节点 52 List<Element> lists = root.elements() ; 53 for (Element ele : lists) { 54 List<Element> elements = ele.elements() ; 55 for (Element element : elements) { 56 if (element.getName().equals("名字")) 57 System.out.println(element.getText()); 58 } 59 } 60 } 61 //2.读取属性值 62 @Test 63 public void getAttribute () { 64 Element root = doc.getRootElement() ; 65 //通过跟节点获取到子节点 66 List<Element> lists = root.elements() ; 67 for(Element ele : lists) { 68 List<Attribute> attributes = ele.attributes(); 69 for (Attribute attribute : attributes) { 70 System.out.println(attribute.getText()); 71 } 72 } 73 } 74 //3.添加节点 75 /* 76 * 在“书”节点下添加“出版日期”节点。 77 */ 78 @Test 79 public void addNode() throws Exception { 80 Element root = doc.getRootElement() ; 81 for (Iterator<Element> it = root.elementIterator(); it.hasNext() ;) { 82 it.next().addElement("出版日期") 83 .setText("2007年") ; 84 } 85 //写到XML文件中去。 86 writeToXML() ; 87 } 88 //4.删除节点 89 /* 90 * 删除“书”节点下,“名字”为“笑傲江湖”的“出版日期”的节点。 91 * 思路: 92 * 1、获取根元素。 93 * 2、通过根元素获取其子元素,。 94 * 3、遍历其子元素,获取子元素的"名字"元素。 95 * 4、如果"名字"元素的值符合条件。 96 * 5、则删除其兄弟元素"出版日期" 。 97 */ 98 @Test 99 public void removeNode() throws Exception{ 100 Element root = doc.getRootElement() ; 101 // 102 for (Iterator<Element> it = root.elementIterator(); it.hasNext() ;) { 103 Element element = it.next() ; 104 if (element.element("名字").getText().equals("笑傲江湖")) 105 element.remove(element.element("出版日期")) ; 106 } 107 writeToXML() ; 108 } 109 public void writeToXML() throws Exception{ 110 111 //在写入XML文件时,要设置写入的编码格式:utf-8 112 OutputFormat format = OutputFormat.createPrettyPrint(); 113 format.setEncoding("utf-8") ; 114 //最好不要用 FileWriter 写入,因为,FileWriter 写入时默认为系统设定的编码! 115 XMLWriter writer = new XMLWriter( 116 new FileOutputStream("src/cn/itheima/xml/day01/book.xml"),format ); 117 writer.write( doc ); 118 writer.close(); 119 } 120 @Before 121 public void getDoc() throws Exception { 122 doc = new SAXReader().read( "src/cn/itheima/xml/day01/book.xml"); 123 } 124 @After 125 public void setDoc() { 126 doc = null ; 127 } 128 }
九、XML约束之schema
l XML Schema 文件自身就是一个XML文件,但它的扩展名通常为.xsd。
l 一个XML Schema文档通常称之为模式文档(约束文档),遵循这个文档书写的xml文件称之为实例文档。
l 和XML文件一样,一个XML Schema文档也必须有一个根结点,但这个根结点的名称为Schema。
l 编写了一个XML Schema约束文档后,通常需要把这个文件中声明的元素绑定到一个URI地址上,在XML Schema技术中有一个专业术语来描述这个过程,即把XML Schema文档声明的元素绑定到一个名称空间上,以后XML文件就可以通过这个URI(即名称空间)来告诉解析引擎,xml文档中编写的元素来自哪里,被谁约束。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!-- 3 在XML Schema中,每个约束模式文档都可以被赋以一个唯一的名称空间, 4 名称空间用一个唯一的URI(Uniform Resource Identifier,统一资源标识符)表示 5 6 名称空间: 7 targetNamespace="http://jbelial.cnblogs.com" 8 URL:http://jbelial.cnblogs.com 根本没有指向任何文件,只是一个分配的名字。 9 10 elementFormDefault="qualified" 11 12 elementFormDefault元素用于指定, 13 该schema文档中声明的根元素及其所有子元素都属于targetNamespace所指定的名称空间。 14 15 --> 16 <schema xmlns="http://www.w3.org/2001/XMLSchema" 17 targetNamespace="http://jbelial.cnblogs.com" 18 xmlns:tns="http://www.example.org/NewXMLSchema" 19 elementFormDefault="qualified"> 20 <!-- 规定根元素 --> 21 <element name='书架'> 22 <!-- 根元素下存放复杂数据类型 --> 23 <complexType> 24 <!-- 根元素下的元素的排列方式,和数目为"未绑定" --> 25 <sequence maxOccurs='unbounded'> 26 <element name='书'> 27 <complexType> 28 <sequence> 29 <!-- 约束:元素的名字,和接收类型: --> 30 <element name="名字" type="string" /> 31 <element name="作者" type="string" /> 32 <element name="价格" type="string" /> 33 </sequence> 34 </complexType> 35 </element> 36 </sequence> 37 </complexType> 38 </element> 39 </schema>
在用Schema 约束XML 文档时,要注意一下问题:
a)你要创建的xml文档中的根标签是什么?
第一个出现的 <xs:element name='书架' > 就是根标签
b) 思考: 你使用这个根标签它来自哪个名称空间.
在schema约束文档中的 targetNamespace="http://jbelial.cnblogs.com" 就是用来说明所有跟标签绑定在哪个目标名称空间上.
c) 思考: 你要引用 schema文档它与目前名称空间的对应关系?
需要在xml文档中添加 xsi:schemaLocation="{namespace} {location}"
{namespace} 就是 : http://jbelial.cnblogs.com
{location} : 引用的schema文档在哪里
d) 固定的写法:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<?xml version="1.0" encoding="UTF-8"?> <p:书架 xmlns:p = "http://jbelial.cnblogs.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jbelial.cnblogs.com NewXMLSchema.xsd"> <p:书> <p:名字>射雕英雄传</p:名字> <p:作者>金庸</p:作者> <p:价格>100.00</p:价格> </p:书> </p:书架>
总结:
XML Schema VS DTD