XML技术
认识XML
- XML是Extensible Markup Language即可扩展标记语言的缩写,是一种简单的数据存储语言,使用一系列的简单标记来描述数据。
- XML特点如下
- XML与操作系统、编程语言的开发平台都无关。
- 规范统一
XML文档结构
<?xml version="1.0" encoding="UTF-8"?>
<books>
<!--图书信息-->
<book id="bk101">
<author>王珊</author>
<title>.NET高级教程</title>
<description>包含c#框架和网络编程等</description>
</book>
<book id="bk102">
<author>李明明</author>
<title>XML基础编程</title>
<decription>包含XML基础概念和基本用法</decription>
</book>
</books>
-
XML声明
<?xml version="1.0" encoding="UTF-8"?>表示XML声明,用以表明该文件是一个XML文档。XML文档总是以XML声明开始,它定义了XML的版本和使用的编码格式等信息。
-
XML声明由以下几个部分组成。
- version:文档符合XML 1.0 规范。
- encoding:文档字符编码,默认为"UTF-8"
- 对于任何一个XML文档,其声明部分都是固定的格式。
- 标签
- 在XML文件中,用尖括号<>括起来的的各种标签(Tag)来标记数据,标签需成对使用来界定字符数据,例如,<author>王珊</author>这一对标签中,<author>是开始标签,</author>是结束标签,"王珊"是标签描述的内容。XML文件可以包含任意数量的标签。
-
根元素
- 每个XML文档必须有且仅有一个根元素,如<books></books>。
- 个元素有如下特点
- 根元素是一个完全包括文档中其他所有元素的元素。
- 根元素的起始标签要放在所有其他元素的其实标签之前。
- 根元素的结束标签要放在所有其他元素的结束标签之后。
-
元素
XML文档的主要部分是元素,元素由开始标签、元素内容和结束标签组成。元素内容可以包含子元素、字符数据等。如<author>王珊</author>就是一个元素。
-
元素的命名规则如下
名称中可以包含字母、数字或者其他字符。
名称不能以数字或者标点符号开始。
名称不能以字符XML(或者xml、Xml)开始。
名称中不能包含空格。
-
注意:XML标签必须成对出现并正确嵌套,如下面的嵌套方式就是不正确的
<title> <name> XML编程 </title> </name> //2.元素允许是空元素,以下元素的写法是允许的 <title></title> <title></title> <title/>
- 属性
- 在描述图书信息的XML文档中, <book id="bk101">标签使用id属性描述图书的编号信息
- 属性定义语法格式如下
- <元素名 属性名="属性值">
- 属性值用一对双引号包含起来
- 属性定义语法格式如下
- 在描述图书信息的XML文档中, <book id="bk101">标签使用id属性描述图书的编号信息
- 注意:1.一个元素可以有多个属性,它的基本格式为<元素名 属性名="属性值" 属性名="属性值"/>,多个属性之间用空格隔开;2.属性值中不能直接包含<、“、&等字符;3.属性可以加在任何一个元素的起始标签上,但不能加在结束标签上
-
XML中的特殊字符处理
-
在XML中,有时在元素的文本中会出现一些特殊字符(如<、>、‘、”、&),而XML文档结构本身就用到了这几个特殊字符,有以下两种办法,可以正确地解析包含特殊字符
-
对着五个特殊字符进行转义,也就是使用XML中预定义实体代替这些字符,XML中地预定义实体和特殊字符地对应关系如下
-
实体名称 字符 &it < > > & & " “ &apos ,
-
-
-
-
如果在元素的文本中有大量的特殊字符,可以使用CDATA节处理。
CDATA节中的所有字符都会被当作元素字符数据的常量部分,而不是XML标签。
-
定义CDATA的语法格式如下。
<![CDATA[ 要显示的字符]]>
-
-
XML中的注释
-
注释的语法格式如下。
<!--注释内容-->
-
-
格式良好的XML文档
- 格式良好的XML文档需要遵循如下规则
- 必须有XML声明语句。
- 必须有且仅有一个根元素。
- 标签大小写敏感。
- 属性值用双引号包含起来。
- 标签成对出现。
- 元素正确嵌套。
- 格式良好的XML文档需要遵循如下规则
XML优势
- XML独立于计算机平台,操作系统和编程语言来表示数据,凭借其简单性、可扩展性、交互性和灵活性在计算机行业中得到了世界范围的支持和采纳。XML基于文本格式,允许开发人员描述结构化数据并在各种应用之间发送和交换这些数据,使得不同系统之间交互数据具备了统一的格式。
- XML的优势主要体现在以下几点
- 数据存储:XML与Orcle和SQL Server等数据库一样,都可以实现数据的持久化存储。XML极其简单,正是这点使XML狱中不同。
- 数据交互:在实际应用中,由于各个计算机所使用的操作系统、数据库不同,一次数据之间的交换很复杂。但现在可以使用XML来交换数据
- 数据配置:许多应用都将配置数据存储在XML文件中。
XML中的命名空间
命名空间在XML文档中可以用作元素或属性名称的名称集合,它们用来表示来自特定域(标准组织、公司、行业)的名称。
-
命名空间的必要性:XML解析器在解析XML文档时,对于重名的元素,可能出现解析冲突。命名空间有助于标准化元素和属性,并为它们加上唯一的标识。
-
声明命名空间:
-
声明命名空间的语法格式如下:
xmlns:[prefix]="[命名空间的URI]"
- prefix是前缀名称,它用作命名空间的别名。
-
- xmlns是保留属性。
-
属性和命名空间
- 除非带有前缀,否则属性属于他们的元素所在的命名空间
XML文档的验证
- 关键步骤如下
- 使用DTD验证XML文档
- 使用Schema验证XML文档
使用DTD验证XML文档
-
使用DTD验证XML
-
DTD简介
-
DTD 是Document Type Definition即文档类型定义的缩写。DTD用来描述XML文档结构,一个DTD文档可能包含如下内容
- 元素的定义规则
- 元素之间的属性
- 属性的定义规则
-
DTD的作用如下
- DTD使每一个XML文件可以携带一个自身格式的描述。
- DTD使不同组织的人可以使用通用DTD来交换数据。
- DTD使应用程序可以使用标准DTD校验从外部接收XML数据是否有效。
-
-
- 声明DTD
- DTD的声明语法格式:<!DOCTYPE 根元素[定义内容]>
- 声明DTD
- DTD元素
- DTD元素的定义语法格式:<!ELEMENT NAME CONTENT>
- DTD元素
- 常见元素类型如下
-
PCDEATA,可以包含任何字符数据,但是不能在其中包含任何子元素,例如:<!ELEMENT title(#PCDATA)>
- 纯元素类型,只包含子元素,并且除这些子元素外没有文本内容,例如:<!ELEMENT poem(poem*)>
-
- 使用外部DTD验证XML
- 前面的例子将DTD嵌入在XML文件中用于验证描述诗集的XML,这被称为内部DTD文档。当验证的XML文件较多,或者待验证的XML文档格式较复杂时,这种方式就不合适了。这时可以把DTD存储在独立的文件中,存储DTD的文件一般以.dtd作为文件的扩展名
- 应用外部DTD文档的语法:<!DOCTYPE 根元素 SYSTEM"DTD 文件路径">。DOCTYPE和SYSTEM是关键字。
使用Schema验证XML文档
- XML Schema针对DTD的不足之处进行了改善,如隐晦的语法、缺乏数据类型、封闭的内容模型以及不支持命名空间等。和DTD相比,XML Schema是使用XML语法编写的,它更易于学习和使用。
- Schema基础概念
- XML Schema是用一套预先规定的XML元素和属性创建的,这些元素和属性定义了XML文档的结构和内容模式。XML Schema规定了XML文档实例的结构和每个元素/属性的数据类型
- Schem包含的数据类型
- 简单类型
- 内置的数据类型,包括基本数据类型和扩展的数据类型
- 用户自定义类型(通过simpleType定义)。
- 复杂类型(通过complexType定义)。
- 简单类型
- Schema常用元素类型
- 根元素:schema。
- 定义元素和属性的元素:element、group、attribute和attributeGrop。
- 定义简单类型的元素:simpleType。
- 定义复杂类型的元素:complexType。
- 进行类型约束的元素:choice、unique、sequence和restriction。
附:关于各种元素的具体用法例子见书第153页
使用DOM解析XML
- 目前XML的解析技术有4种
- DOM
- DOM是基于XML的树结构来完成解析的,DOM解析XML文档是,会根据读取的文档,构建一个驻留内存的树结构,然后就可以使用DOM接口来操作这个树结构。因为整个文档的数据结构是驻留在内存中的,所以操作非常方便。它还支持删除、修改、重新排列的多种操作。DOM解析XML的方式非常适用于多次访问XML应用程序但是DOM解析比较消耗资源。
- DOM
-
SAX
- SAX是基于事件解析的,它是为了解决DOM解析的资源消耗问题而出现的。它不像DOM那样需要建立一棵完整的文档树,而是通过事件处理器完成对文档的解析。。因为SAX解析不用事先调入整个文档,所以它的优势就是资源占用少,内存消耗小。一般在解析数据量比较大的XML文档时会采用这种方式。
- JDOM
-
JDOM的目的是直接为Java编程服务,利用春Java技术对XML文档实现解析、生成、序列化以及其他操作,把SAX和DOM的功能有效结合起来,简化与XML的交互并且比使用DOM更快。JDOM与DOM有两方面不同,首先,JDOM仅使用具体类而不是用接口,在这方面简化了API,但也限制了灵活性;其次,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。JDOM的优势在于"使用20%的经历解决80%的问题"。
- DOM4J
- DOM4J是一个非常优秀的Java XML API,具有性能优异、功能强大和易用的特点,同时它也是一个开放源代码的软件。
附:关于DOM和DOM4J的具体用法详见课本。。。
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="1">
<name>老人与海</name>
<author>海明威</author>
</book>
<book id="2">
<name>茶花女</name>
<author>小仲马</author>
</book>
</books>
一、使用DOM解析XML节点
解析XML文件需要使用几个类,我们直接通过代码来看一下。
1、加载解析XML文件
先创建一个DocumentBuilderFactory
对象,然后通过该对象的newDocumentBuilder()
方法创建一个DocumentBuilder
对象,这里需要捕获一下异常。然后通过该对象的parse()方法解析XML文件,该方法接收一个文件路径的参数(默认项目根目录)。
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse("books.xml");
}catch (Exception e){
e.printStackTrace();
}
2、获取节点属性
这里我们要获取每一个book节点,接下来的方法就js的DOM操作差不多了。获取到的所有节点对象一个是NodeList
类型。我们可以通过该对象的getLength()
方法获取到该节点的个数。
NodeList bookList = document.getElementsByTagName("book");
System.out.println(bookList.getLength()); // 2
以上获取到的是一个节点的集合,我们可以通过遍历来获取每一个节点。这里用到的是NodeList
对象的item()
方法。
for(int i = 0; i < bookList.getLength(); i++){
Node book = bookList.item(i);
}
上面的xml文件中的每个book节点都有一个id属性,我们来获取它。这里有两种常见的情况,在属性名未知的时候,可以先获取所有属性再进行遍历:
NamedNodeMap attrs = book.getAttributes();
for (int j = 0; j < attrs.getLength(); j++){
Node attr = attrs.item(j);
System.out.println(attr.getNodeName() + ":" + attr.getNodeValue());
}
getAttributes()
方法获取所有属性是一个NamedNodeMap 类型的对象,然后她也有getLength()
方法和item()
方法,由此可以获取到该节点的每一个属性,是一个Node类型的对象。最后通过该对象的对应方法获取属性名和属性值。
感觉好繁琐呀,如果我已经知道这个节点有一个id属性呢?通过节点集合的item方法获取到一个Element
类型的对象,这里需要强转一下类型。直接给该对象的getAttribute()
方法传递一个属性名参数就可以获取到其属性值。
Element book = (Element) bookList.item(i);
String attrValue = book.getAttribute("id");
System.out.println(attrValue);
3、获取节点的子节点
每一个book节点又有子节点,我们来看一下这么获取:通过节点的getChildNodes()
方法获取子节点集合对象。同样通过item()
方法获取每一个子节点。
NodeList childNodes = book.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++){
System.out.println(childNodes.item(j).getNodeName()); //打印每一个子节点名
}
这里注意,空格会被当做是Text类型的节点。打印的节点名永远都是#text。
节点名称 | NodeType | nodeName的返回值 | nodeValue的返回值 |
---|---|---|---|
Element | 1 | 节点的标签名 | null |
Attr | 2 | 属性名称 | 属性值 |
Text | 3 | #text | 节点内容 |
所以这里我们通过节点类型来判断一下,以便我们获取到真正的标签节点。
// 只要Element 类型的节点
if(childNodes.item(j).getNodeType() == Node.ELEMENT_NODE){
// ...
}
4、获取节点的内容
这里也有两种常见的情况,节点中文本内容前没有其他子节点的时候:通过getFirstChild()
获取到第一个节点(其实就是文本节点),然后getNodeValue()
得到文本节点的内容。这种方法算是碰巧能取到内容。
String text = childNodes.item(j).getFirstChild().getNodeValue();
如果节点中还有节点怎么,比如上面的name节点改一下:<name><a>书名:</a>老人与海</name>
。可以通过getTextContent()
方法获取节点下的所有文本内容。直接把标签节点忽略。
String text2 = childNodes.item(j).getTextContent();
System.out.println(text2); //书名:老人与海
二、SAX解析xml文件
SAX解析需要先创建一个继承于DefaultHandler
的类。通过重写方法来对XML文件进行解析。
public class SAXHandler extends DefaultHandler {
//遍历开始标签
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
}
//遍历结束标签
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
}
//用来标识解析开始
@Override
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("解析开始了");
}
//标识解析结束
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
// 可以用来获取节点中的文本
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
}
}
1、解析属性
我们在遍历开始标签的startElement方法里,来解析节点。参数qName就是标签名,attributes是所有的属性。
// 如果开始解析book元素
if(qName.equals("book")){
// 获取指定属性的值
String value = attributes.getValue("id");
System.out.println(value);
// 遍历属性,获取属性名和属性值
for(int i = 0; i < attributes.getLength(); i++){
System.out.println(attributes.getQName(i));
System.out.println(attributes.getValue(i));
}
}
2、解析节点的文本内容
在characters方法中,第一个参数其实是该文件的所有内容,第二个参数是在解析完一个开始标签之后的位置,第三个参数是开始标签之后到结束标签之前的长度。由此我们可结合这三个参数获取到节点的文本内容。
String text = new String(ch, start, length);
// 通过判断过滤一下空格文本节点
if (!text.trim().equals("")){
System.out.println(text); //节点中的文本
}
创建实例调用方法
然后我们在实际代码中,创建newSAXParser对象,通过的parse方法进行解析,第一个参数是文件路径,第二个参数就是一个刚才创建的SAXHandler
类的实例。
SAXParserFactory spf = SAXParserFactory.newInstance();
try {
SAXParser sp = spf.newSAXParser();
SAXHandler sh = new SAXHandler();
sp.parse("books.xml", sh);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
三、JDOM解析
JDOM并不是官方提供的方法,所以需要手动添加jar包。通过build方法解析的时候可以传入一个输入流。然后其他就不赘述了,思想都一样,直接代码一看就明白。
1、属性的解析
SAXBuilder saxBuilder = new SAXBuilder();
try {
FileInputStream fis = new FileInputStream("books.xml");
Document document = saxBuilder.build(fis);
// 获取根节点
Element rootElement = document.getRootElement();
// 获取根节点下子节点的集合
List<Element> bookList = rootElement.getChildren();
// 遍历节点
for (Element book: bookList) {
List<Attribute> attributes = book.getAttributes();
// 遍历属性
for (Attribute attr: attributes) {
System.out.println(attr.getName() + ":" + attr.getValue());
}
//通过属性名获取属性值
book.getAttributeValue("id");
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2、子节点的解析
通过getChildren获取子节点,然后通过getName()获取节点名,getValue获取节点中的文本内容。
List<Element> bookChilds = book.getChildren();
for (Element ele : bookChilds) {
System.out.println(ele.getName()+":"+ele.getValue());
}
name:老人与海
author:海明威
name:茶花女
author:小仲马
3、补充:乱码问题
如果解析碰到乱码,先修改xml文件的编码格式,如果还是达不到自己想要的效果,我们可以通过在创建输入流的时候指定编码格式。
4、存到java对象
首先创建一个book对象,get和set方法就不写了。
public class Book {
private String id;
private String author;
private String name;
private String isHot;
}
然后在遍历属性和子节点的时候,将对应的值添加到Book对象中,遍历完一次之后添加到一个List中,然后清空进行下一次遍历。
ArrayList<Book> booksList = new ArrayList<Book>();
SAXBuilder saxBuilder = new SAXBuilder();
try {
Document document = saxBuilder.build("books.xml");
// 获取根节点
Element rootElement = document.getRootElement();
// 获取根节点下子节点的集合
List<Element> bookList = rootElement.getChildren();
// 遍历节点
Book b = new Book();
for (Element book: bookList) {
List<Attribute> attributes = book.getAttributes();
for (Attribute attr : attributes) {
if (attr.getName() == "id"){
b.setId(attr.getValue());
}
if (attr.getName() == "isHot"){
b.setIsHot(b.getIsHot());
}
}
List<Element> bookChilds = book.getChildren();
for (Element ele : bookChilds) {
if (ele.getName() == "author"){
b.setAuthor(ele.getValue());
}
if(ele.getName() == "name"){
b.setName(ele.getValue());
}
}
booksList.add(b);
b = null;
}
System.out.println(booksList);
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
四、DOM4J解析
同样需要手动添加包到项目。
1、属性解析
这里不同的是从根节点下获取到一个book节点的遍历器。
SAXReader reader = new SAXReader();
// 加载xml文件,获取document对象
try {
Document document = reader.read(new File("books.xml"));
// 获取根节点
Element books = document.getRootElement();
// 获取迭代器
Iterator it = books.elementIterator();
while (it.hasNext()){
Element book = (Element)it.next();
List<Attribute> bookAttrs = book.attributes();
for(Attribute attr : bookAttrs){
System.out.println(attr.getName() +":"+ attr.getValue());
}
}
} catch (DocumentException e) {
e.printStackTrace();
}
2、解析子节点
上面从根节点获取到一个book子节点的遍历器,这里同样通过book节点的elementIterator方法获取到一个book节点下子节点的遍历器。
Iterator itt = book.elementIterator();
while (itt.hasNext()){
Element bookChild = (Element) itt.next();
System.out.println(bookChild.getName());
}
XML生成
一、DOM方法
首先和解析XML文件一样需要创建一些对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document document = db.newDocument();
创建一个根节点和一个子节点
Element books = document.createElement("books");
Element book = document.createElement("book");
给节点添加属性:
book.setAttribute("id", "0");
给节点添加文本内容:
book.setTextContent("安娜卡列尼娜");
把子节点插入到根节点,并且把根节点插入到document
树:
books.appendChild(book);
document.appendChild(books);
最后把document
树写进xml文件中:
TransformerFactory tf = TransformerFactory.newInstance();
try {
Transformer transformer = tf.newTransformer();
// 把节点数写进文件中
transformer.transform(new DOMSource(document), new StreamResult(new File("myBooks.xml")));
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}catch (TransformerException e) {
e.printStackTrace();
}
SAX
。。。
DOM4J
这里先了解一下RSS
,它是XML的一种典型应用。是一种描述和同步网站内容的格式。
// 创建document对象
Document document = DocumentHelper.createDocument();
// 创建根节点
Element rss = document.addElement("rss");
rss.addAttribute("version", "2.0");
XMLWriter writer = null;
try {
writer = new XMLWriter(new FileOutputStream(new File("rss.xml")));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
writer.write(document);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
此时我们已经成功创建了xml文件,
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"/>
那么接下我们继续创建子节点和内容,调用创建出来的节点对象的addElement
就是创建子节点,setText
方法是设置内容。
// 生成子节点和内容
Element channel = rss.addElement("channel");
Element title = channel.addElement("title");
title.setText("我是内容");
此时生成的文件还是没有一个很好的格式,我们来设置一下:
// 设置想xml格式(缩进、换行、编码)
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK"); // 可以设置编码,默认是UTF-8
// 在XMLWriter的构造函数中,将format 对象当做第二个参数传入
writer = new XMLWriter(new FileOutputStream(new File("rss.xml")), format);
这样我们的xml文件的格式就非常美观了
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>我是内容</title>
</channel>
</rss>
需要注意,如果有特殊字符的情况下默认会给我们进行转义的,比如
title.setText("<我是内容");
// 最后生成时这样的
<title><我是内容</title>
想要禁止自动转义也很简单,调用writer对象的setEscapeText()方法设置为false就OK了。
writer.setEscapeText(false);
//这时生成的内容就是没有转义的
<title><我是内容</title>
JDOM
先创建根节点,添加到document。
// 创建根节点并添加属性,插入到document
Element rss = new Element("rss");
rss.setAttribute("version", "2.0");
Document document = new Document(rss);
// 将document对象转换成xml文档
XMLOutputter xo = null;
try {
xo = new XMLOutputter();
} catch (Exception e) {
e.printStackTrace();
}
try {
xo.output(document, new FileOutputStream(new File("rssNew.xml")));
} catch (IOException e) {
e.printStackTrace();
}
创建子节点及子节点的文本,
Element channel = new Element("channel");
rss.addContent(channel);
Element title = new Element("title");
title.setText("我是内容");
channel.addContent(title);
设置格式,也是创建一个format对象然后传入XMLOutputter的构造函数。
Format format = Format.getCompactFormat();
format.setIndent(""); //设置换行
format.setEncoding("GBK"); //设置编码
XMLOutputter xo = new XMLOutputter(format);