再讲XML解析前,先问读者一个小问题:在使用setter和getter方法时,设置与获取到的属性值与我们直接在构造对象时的值是否一样?这个问题很重要,因为这儿很容出现bug。
public class Student{
private String name;
public Student(String name){
name = this.name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
五,XML解析
XML解析有两种原理:SAX解析和DOM解析。①SAX解析:社区开发一套基于事件驱动的解析方式,逐行解析,性能好,但是不能重复使用数据,如果要修改,或者回头查数据,必须重新从头读取数据;②DOM解析:官方定义的一套规则,把整个xml文件读取到内存,重复使用,消耗较多的内存,可以重复使用和修改数据(原理:读到内存后形成一个Document文档,对象形成一个DOM树,对xml文件的操作实际上是对Document对象进行操作,最后再做数据同步操作),缺点是只能解析小型xml文件,文件过大会导致内存溢出。
XML解析的方式:①jdom解析(已被淘汰);②dom4j:通过SAX读取xml,形成一个·DOM存储在内存中;③jaxb:Java API for XML Bind,负责XML文档对象的自动绑定,底层使用jaxp解析XML,是基于注解开发的;④jaxp:Java API for XML Parser,Java5.0以后提供一套SPI,负责解析XML。
<一>dom4j解析
使用dom4j解析需要先在项目中导入dom4j的jar包(小编使用的是Eclipse),同时添加依赖(Build Path),同时这儿用junit4来做的测试。下面代码演示解析DTD和Schema两种xml文件。
小编的项目目录参考:(emm,那个example.xml文档是之前用来练习用的,这儿没用到)
代码逻辑:
1,定义一个测试类demoTest,这个类负责测试;
2,定义一个与数据链接的类DAO_demo,这个类负责数据处理;
3,定义两个数据产生类,分别为Book类和Student类;
4,在·src目录下建一个sources目录(为了将资源与源代码分开,实际上在电脑目录下查看,资源和java源代码任然是在一个根目录下),用来放bookstore.xml,bookstore.dtd,student.xml,student.xsd文件,
@这儿是xml文件的代码
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT bookstore (book+)>
<!ELEMENT book (name,author,price)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!--注意只有一种数据类型:字符串-->
<!ELEMENT price (#PCDATA)>
<!--注意id必须以字母开头 -->
<!ATTLIST book id ID #REQUIRED>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore SYSTEM "bookstore.dtd">
<!-- 如果价格写成double型,那么就要注意数据的转换了,否则要报错!!! -->
<bookstore>
<book id="a1">
<name>水浒传</name>
<author>施耐庵</author>
<price>60</price>
</book>
<book id="a2">
<name>红楼梦</name>
<author>曹雪芹</author>
<price>75</price>
</book>
<book id="a3">
<name>悲惨世界</name>
<author>雨果</author>
<price>30</price>
</book>
</bookstore>
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.yby.org/students"
xmlns:tns="http://www.yby.org/students"
elementFormDefault="qualified">
<element name="students">
<complexType>
<sequence>
<element name="student" minOccurs="1" maxOccurs="unbounded">
<complexType>
<sequence>
<element name="name" type="string" minOccurs="1" maxOccurs="1"/>
<element name="sex">
<simpleType>
<annotation>
<documentation>只能为男或者女</documentation>
</annotation>
<restriction base="string">
<enumeration value="男"/>
<enumeration value="女"/>
</restriction>
</simpleType>
</element>
<element name="age" minOccurs="1" maxOccurs="1">
<simpleType>
<annotation>
<documentation>年龄必须在15~50岁</documentation>
</annotation>
<restriction base="int">
<minInclusive value="15"/>
<maxInclusive value="50"/>
</restriction>
</simpleType>
</element>
<element name="subjects" minOccurs="1" maxOccurs="1">
<complexType>
<sequence>
<element name="subject" minOccurs="1" maxOccurs="3" type="string">
<annotation>
<documentation>主修科目最少为一门,最多为三门</documentation>
</annotation>
</element>
</sequence>
</complexType>
</element>
</sequence>
<!--注意属性约束的位置 -->
<attribute name="id" type="int" use="required"/>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>
<?xml version="1.0" encoding="UTF-8"?>
<students xmlns="http://www.yby.org/students"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.yby.org/students student.xsd ">
<student id="1">
<name>张三</name>
<sex>男</sex>
<age>17</age>
<subjects>
<subject>物理</subject>
<subject>数学</subject>
</subjects>
</student>
<student id="2">
<name>李四</name>
<sex>男</sex>
<age>19</age>
<subjects>
<subject>英语</subject>
</subjects>
</student>
<student id="3">
<name>王二</name>
<sex>男</sex>
<age>27</age>
<subjects>
<subject>化学</subject>
</subjects>
</student>
<student id="4">
<name>渣渣辉</name>
<sex>男</sex>
<age>36</age>
<subjects>
<subject>物理</subject>
</subjects>
</student>
<student id="5">
<name>达摩</name>
<sex>男</sex>
<age>34</age>
<subjects>
<subject>线性代数</subject>
</subjects>
</student>
<student id="6">
<name>沸羊羊</name>
<sex>男</sex>
<age>34</age>
<subjects>
<subject>离散数学</subject>
<subject>恋爱心理学</subject>
</subjects>
</student>
</students>
@这儿是数据产生类
public class Book {
private String name;
private String author;
private int price; //注意数据类型转换
private String id; //由于DTD约束中id只能为字符串
public Book() {
}
@Override
public String toString() {
return "Book [name=" + name + ", author=" + author + ", price=" + price + ", id=" + id + "]";
}
public Book(String name,String author,int price,String id) {
name = this.name;
author = this.author;
price = this.price;
id = this.id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public class Student {
private String id;
private String name;
private String sex;
private String age;
private String subject1;
private String subject2;
private String subject3;
public Student() {
}
public Student(String id,String name,String sex,String age,String subject1) {
id = this.id;
name = this.name;
sex = this.sex;
age = this.age;
subject1 = this.subject1;
}
public Student(String id,String name,String sex,String age,String subject1,String subject2) {
id = this.id;
name = this.name;
sex = this.sex;
age = this.age;
subject1 = this.subject1;
subject2 = this.subject2;
}
public Student(String id,String name,String sex,String age,String subject1,String subject2,String subject3) {
id = this.id;
name = this.name;
sex = this.sex;
age = this.age;
subject1 = this.subject1;
subject2 = this.subject2;
subject3 = this.subject3;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSubject1() {
return subject1;
}
public void setSubject1(String subject1) {
this.subject1 = subject1;
}
public String getSubject2() {
return subject2;
}
public void setSubject2(String subject2) {
this.subject2 = subject2;
}
public String getSubject3() {
return subject3;
}
public void setSubject3(String subject3) {
this.subject3 = subject3;
}
}
@这儿是数据处理类:代码里详细讲解了开头的那个问题以及bug的解决思路,在阅读代码之前希望读者可以先看看导入包的各个类和方法,对理解代码的逻辑有很大帮助!!
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
//import org.junit.Test;
public class DAO_demo {
//dom4j解析
//注意抛异常
public Document getDocument(String document) throws DocumentException {
//第一步:获取xml文件输入流(硬盘————>内存)
InputStream input = this.getClass().getClassLoader().getResourceAsStream(document);
//第二步:获取SAX解析对象
SAXReader reader = new SAXReader();
//第三步:获取Document对象(不能直接获取到Document对象————单例设计模式)
Document doc = reader.read(input);
return doc;
}
//查询信息————bookstore.xml(DTD约束) && student.xml(schema约束) 的查询方法一样
public List<Book> QueryBook() throws DocumentException {
Element root = this.getDocument("bookstore.xml").getRootElement(); //获取根元素
List<Element> listElement = root.elements("book"); //elements()方法返回的是一个集合
List<Book> listBook = new ArrayList<>();
for(Element bookEL : listElement) {
String name = bookEL.elementText("name");
String author = bookEL.elementText("author");
int price =Integer.parseInt(bookEL.elementText("price"));
//id是属性,获取方式不同于获取元素
Attribute arr = bookEL.attribute("id");//获取属性对象
String id = arr.getValue(); //获取属性值
/* //当用下面的代码替换59~64的代码时,listBoook将是一个null对象
* // name author price id 都获得了值但为什么listBook是一个null??
* System.out.println(name);
* Book book = new Book(name,author,price,id);
* listBook.add(book);
* 原因是:没有设置属性值,因此获取不到book对象的值,所以如果采用上面的方式实例book对象
* 就必须用get()方法去获取相应的属性值
*
**/
Book book = new Book();
book.setName(name);
book.setAuthor(author);
book.setPrice(price);
book.setId(id);
listBook.add(book);
}
return listBook;
}
//添加信息—————student.xml(schema约束),向bookstore.xml(DTD约束)也是差不多一样的
public void SaveStudent(Student stu) throws DocumentException,IOException{
//Student stu = new Student("4","渣渣辉","男","26","物理");
//为什么stu不为null (输出为 : Student@7de26db8 ) ,但是却获取不到属性值???
//System.out.println(stu.getId());
Document doc = this.getDocument("student.xml");
Element root = doc.getRootElement();
// 这儿有个小问题:addAttribute(String QName,String args)
// 这个方法的参数全是字符串(所以把id,sex在Student类中都用String类型,String转int是比较方便地)
//根据上面的bug分析知道,要获取属性值,必须先设置属性值,否则就是一个null对象
Element element = root.addElement("student").addAttribute("id", stu.getId());
element.addElement("name").setText(stu.getName());
element.addElement("sex").setText(stu.getSex());
element.addElement("age").setText(stu.getAge());;
element.addElement("subjects").addElement("subject").setText(stu.getSubject1());
if(stu.getSubject2() != null) {
/* 如果这儿这样写 : element.addElement("subjects").addElement("subject").setText(stu.getSubject2());
* 会导致在xml文件中写出的格式是是下面那样
* <subjects>
* <subject>离散数学</subject>
* </subjects>
* <subjects>
* <subject>恋爱心理学</subject>
* </subjects>
* 解决办法:先获取subjects节点————element.element("subjects"),然后在该节点下添加subject元素
*/
element.element("subjects").addElement("subject").setText(stu.getSubject2());
}
if(stu.getSubject3() != null) {
element.element("subjects").addElement("subject").setText(stu.getSubject3());
}
//同步数据
/*
* 这儿有两种方式去同步数据
* 第一种:无格式同步
* OutputStream out = new FileOutputStream("sources/student.xml");
* XMLWriter writer = new XMLWriter(out);
* writer.write(doc);
* writer.close();
* 第二种:格式化同步,这种方式同步的数据很美观(这儿用的是此种方式)
*/
OutputStream out = new FileOutputStream("sources/student.xml");
OutputFormat format = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(out,format);
writer.write(doc);
writer.close(); //使用完流后后一定要关闭流,否则非正常关闭流会导致数据丢失!!!!
}
/* 在上面添加信息中,会发现一个问题,如果我们添加的信息不符合约束,但也能被添加到文件中
* 因此在添加信息之前我们应该先对所添加的信息进行验证,检查所添加信息是否符合约束,验证两个问题
* 一,验证xml文件是否使用了约束,如果没有则报错
* 二,验证xml文件的元素是否符合约束,不符合则报错
* 有两种验证方法,下面分别演示(DTD约束与Schema约束的验证差不多,这儿演示后者)
* validation_1() 手动加载xml约束
* validation_2() 使用URL来读取xml文件,会自动去加载对应的约束
*/
public void validation_1(String xml,String xsd) throws SAXException, DocumentException {
InputStream in = this.getClass().getClassLoader().getResourceAsStream(xml);
SAXReader reader = new SAXReader();
reader.setEntityResolver(new EntityResolver() {
@Override
public InputSource resolveEntity(String arg0, String arg1) throws SAXException, IOException {
InputStream xsdStream = this.getClass().getClassLoader().getResourceAsStream(xsd);
InputSource source = new InputSource(xsdStream);
return source;
}
});
reader.setValidation(true); //开启验证
//schema约束必须加上下面的这行代码,而且固定的,如果是dtd约束则不用加下面的这行代码
reader.setFeature("http://apache.org/xml/features/validation/schema", true);
Document doc = reader.read(in);
}
public void validation_2(String xml) throws SAXException, DocumentException {
URL xmlURL = this.getClass().getClassLoader().getResource(xml);
SAXReader reader = new SAXReader();
reader.setValidation(true);
//schema约束必须加上下面的这行代码,而且固定的,如果是dtd约束则不用加下面的这行代码
reader.setFeature("http://apache.org/xml/features/validation/schema", true);
Document doc = reader.read(xmlURL);
}
}
@这儿是测试类:有关java测试类的使用下期再讲
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.List;
import org.dom4j.DocumentException;
import org.junit.Test;
import org.xml.sax.SAXException;
public class demoTest {
DAO_demo demoTest;
//查询bookstore.xml的信息
@Test
public void testQueryBook() throws DocumentException {
demoTest = new DAO_demo();
List<Book> list = demoTest.QueryBook();
for(Book book:list) {
System.out.println(book.toString());
}
}
//向student.xml中添加信息
@Test
public void testSaveStudent() throws DocumentException, IOException{
/*如果采用以下方式就会为null,同时会报错———— “不能向XML文件写null”
* Student stu = new Student("4","渣渣辉","男","36","物理");
* demoTest = new DAO_demo();
* demoTest.SaveStudent(stu);
* 原因:没有设置属性值,虽然new的student对象中有值,但是那些值便不是属性值,他们是有缺别的
*/
Student stu = new Student();
//必须设置属性的值,否则为null
stu.setId("4");
stu.setName("渣渣辉");
stu.setSex("男");
stu.setAge("36");
stu.setSubject1("物理");
demoTest = new DAO_demo();
demoTest.SaveStudent(stu);
}
//使用xml验证对添加信息进行验证
@Test
public void testValidation() throws DocumentException, IOException, SAXException {
Student stu = new Student();
//必须设置属性的值,否则为null
stu.setId("6");
stu.setName("沸羊羊");
stu.setSex("男");
stu.setAge("34");
stu.setSubject1("离散数学");
stu.setSubject2("恋爱心理学");
demoTest = new DAO_demo();
demoTest.SaveStudent(stu);
//demoTest.validation_1("student.xml", "student.xsd");
demoTest.validation_2("student.xml");
}
}