JAXP:
█ JAXP:(Java API for XML Processing)开发包是JavaSE的一部分,它由以下几个包及其子包组成:
☞org.w3c.dom:提供DOM方式解析XML的标准接口
☞org.xml.sax:提供SAX方式解析XML的标准接口
☞javax.xml:提供了解析XML文档的类
█ javax.xml.parsers包中,定义了几个工厂类。我们可以通过调用这些工厂类,得到对XML文档进行解析的DOM和SAX解析器对象。
☞DocumentBuilderFactory
☞SAXParserFactory
使用JAXP进行DOM解析
█ javax.xml.parsers 包中的DocumentBuilderFactory用于创建DOM模式的解析器对象 , DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会自动创建一个工厂的对象并返回。
获得JAXP中的DOM解析器
☞调用 DocumentBuilderFactory.newInstance() 方法得到创建 DOM 解析器的工厂。
☞调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象。
☞调用 DOM 解析器对象的 parse() 方法解析 XML 文档,得到代表整个文档的 Document 对象,进行可以利用DOM特性对整个XML文档进行操作了。
DOM编程 | Node对象 |
█ DOM模型(document object model)
☞DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点) document 。
☞在dom中,节点之间关系如下:
•位于一个节点之上的节点是该节点的父节点(parent)
•一个节点之下的节点是该节点的子节点(children)
•同一层次,具有相同父节点的节点是兄弟节点(sibling)
•一个节点的下一个层次的节点集合是节点后代(descendant)
•父、祖父节点及所有位于节点上面的,都是节点的祖先(ancestor)
|
█ Node对象提供了一系列常量来代表结点的类型,当开发人员获得某个Node类型后,就可以把Node节点转换成相应的节点对象(Node的子类对象),以便于调用其特有的方法。(查看API文档)
█ Node对象提供了相应的方法去获得它的父结点或子结点。编程人员通过这些方法就可以读取整个XML文档的内容、或添加、修改、删除XML文档的内容了。
|
DOM方式解析XML文件:
1、得到某个具体的节点内容
2、打印某节点的所有元素节点
|
3、修改某个元素节点的主体内容
4、向指定元素节点中增加子元素节点
|
5、向指定元素节点上增加同级元素节点
6、删除指定元素节点
7、操作XML文件属性
|
更新XML文档:
█ javax.xml.transform包中的Transformer类用于把代表XML文件的Document对象转换为某种格式后进行输出,例如把xml文件应用样式表后转成一个html文档。利用这个对象,当然也可以把Document对象又重新写入到一个XML文件中。
█ Transformer类通过transform方法完成转换操作,该方法接收一个源和一个目的地。我们可以通过:
• javax.xml.transform.dom.DOMSource类来关联要转换的document对象,
• 用javax.xml.transform.stream.StreamResult 对象来表示数据的目的地。
█ Transformer对象通过TransformerFactory获得。
|
//jaxp dom解析的标准写回流程
TransformerFactory tsf =TransformerFactory.newInstance();
Transformer tsTransformer = tsf.newTransformer();
tsTransformer.transform(
new DOMSource(document),
new StreamResult("src/books.xml"));
|
Books.xml | JAXPDomPaser.java |
<?xml version="1.0" encoding="utf-8" ?>
<书架>
<书>
<书名 id="001">浪潮之巅</书名>
<作者>吴军</作者>
<售价>29</售价>
</书>
<书>
<书名 id='002'>数学之美</书名>
<作者>吴军</作者>
<售价><第一版>29</第一版></售价>
</书>
</书架>
|
package com.java.xmldom;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document; //注意是这个包
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
|
public class JAXPDomPaser {
public static void main(String[] args) throws Exception { //真正开发中不能使用
//多态,返回的是内部实现的一个子类的实例;DOM解析器的工厂
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//DOM解析器对象
DocumentBuilder documentBuilder=dbf.newDocumentBuilder();
//绝对路径和相对路径都可以;拿到文档对象模型:DOM树;DOM对象
Document document=documentBuilder.parse("Books.xml");
//解析流程
|
//1、得到某个具体的节点内容 得到第一本书的书名
NodeList booknameList = document.getElementsByTagName("书名");
System.out.println("JAXDomPaser.main()"+booknameList.getLength());
Node bookname1=booknameList.item(0);
short nodeType=bookname1.getNodeType(); //元素节点,节点类型=1
String nodeName = bookname1.getNodeName();
String nodeValue = bookname1.getNodeValue();
String textContent=bookname1.getTextContent();
System.out.println("JAXPDomPaser.main() "+ nodeType + nodeName + nodeValue+textContent);
|
//打印某节点的所有元素节点 ,第二个节点
NodeList bookList = document.getElementsByTagName("书");
//拿到第二本书
Node book2=bookList.item(1);
NodeList childNodes=book2.getChildNodes(); //这里空格也算一个子节点;
System.out.println("第二本书的子元素个数:"+childNodes.getLength());
for(int i=0;i<childNodes.getLength();++i){
if(childNodes.item(i).getNodeType()==1){
String childnodename = childNodes.item(i).getNodeName();
System.out.println("JAXPDomPaser.main () "+childnodename);
}
}
|
//修改某个元素节点的主体内容,把第一本书的作者改成梅浩
Node book1=bookList.item(0);
book1.getChildNodes().item(3).setTextContent("梅浩"); //这里要写回XML才会生效
|
//向指定元素节点上增加同级元素节点
Element newElement=document.createElement("销量"); //这里注意,新建的元素只有一个,添加完就没有了;比如给每个学生添加一个性别属性,用for循环添加,最只会把新建的节点添加到最后一个for循环满足添加的节点,然后写入xml
newElement.setTextContent("1000");
book1.appendChild(newElement);
|
//删除指定元素节点
Node shoujianode=document.getElementsByTagName("售价").item(0);
book1.removeChild(shoujianode);
|
//操作XML文件属性
String xmlEncoding=document.getXmlEncoding();
String version=document.getXmlVersion();
boolean standalong=document.getXmlStandalone();
System.out.println("xml encoding: "+xmlEncoding+"; xml version: "+version+"; xml standalong: "+standalong);
|
//把内存中的dom树输出到磁盘中的文档的标准写法;只有增加、修改才会写入
TransformerFactory tsf =TransformerFactory.newInstance();
Transformer tsTransformer = tsf.newTransformer();
tsTransformer.transform(
new DOMSource(document),
new StreamResult("src/Books.xml"));
}
}//xml会重新保存在src下
|
xml写绝对路劲就直接放到settings文件夹下 | 更改过xml保存会放到src目录下 |
((Element)studentList.item(k)).setAttribute("ID",arg); //找到指定地点,添加一个属性并设置值;这个是Element里面的方法
SAX解析: //一般用来读,写和修改还是用DOM
█ 在使用 DOM 解析 XML 文档时,需要读取整个 XML 文档,在内存中构架代表整个 DOM 树的Doucment对象,从而再对XML文档进行操作。此种情况下,如果 XML 文档特别大,就会消耗计算机的大量内存,并且容易导致内存溢出。
█ SAX解析允许在读取文档的时候,即对文档进行处理,而不必等到整个文档装载完才会文档进行操作。
█ SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,涉及两个部分:解析器和事件处理器:
☞解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
☞解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
☞事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。
//事件处理器有一个缺省的DefaultHandler |
//方法步骤
☞使用SAXParserFactory创建SAX解析工厂
SAXParserFactory spf = SAXParserFactory.newInstance();
☞通过SAX解析工厂得到解析器对象
SAXParser sp = spf.newSAXParser();
☞通过解析器对象得到一个XML的读取器
XMLReader xmlReader = sp.getXMLReader();
☞设置读取器的事件处理器
xmlReader.setContentHandler(new BookParserHandler());
☞解析xml文件
xmlReader.parse("book.xml");
|
Books.xml | MySaxParseDemo.java |
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<书架>
<书>
<书名 id="001">浪潮之巅</书名>
<作者>梅浩</作者>
</书>
<书>
<书名 id="002">数学之美</书名>
<作者>吴军</作者>
<售价><第一版>29</第一版></售价>
</书>
</书架>
|
package com.java.xml_sax;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class MySaxParseDemo {
public static void main(String[] args) throws Exception {
|
SAXParserFactory spf=SAXParserFactory.newInstance();
SAXParser saxParser=spf.newSAXParser();
MySaxhandler mySaxHandler=new MySaxhandler();
// parse(String uri, DefaultHandler dh)
//Parse the content described by the giving Uniform Resource Identifier (URI) as XML using the specified
//使用指定的 DefaultHandler 将给定统一资源标识符 (URI) 描述的内容解析为 XML。
saxParser.parse("Books.xml",mySaxHandler);
//调用这个方法要传递一个缺省DefaultHandler dh ,我们要自己写一个类来重写这个方法
}
}
|
class MySaxhandler extends DefaultHandler{ //定义自己的类来重写方法
//写好类名和继承,右键-源码-覆盖/实现方法选择要覆盖的方法
int count=0; //统计书名出现的次数,经常用这种方法来设置输出制定信息
public void startDocument() throws SAXException {
super.startDocument();
System.out.println("MySaxHandler.startDocument()!文档开始");
}
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("MySaxHandler.endDocument()!文档解析结束");
}
|
public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException {
super.startElement(arg0, arg1, arg2, arg3); //鼠标放上面可以自动显示父类对应的参数名,应该是版本问题不能关联源代码。不然的话就不会显示arg了,那样可以直观看出每个参数的表示意义:startElement(String uri, String localName, String qName, Attributes attributes)
System.out.println("MySaxHandler.startElement()!元素 "+arg2+" 节点开始");
if(arg2=="书名"){
String value=arg3.getValue("id"); //通过属性名查找属性值
String value1=arg3.getValue(0); //通过属性的次序查找属性值,书名里面第0个属性值
System.out.println("id= "+value);
System.out.println("id1= "+value1);
}
if(arg2=="书名"){ //标记第二本书
count++;
}
}
public void endElement(String arg0, String arg1, String arg2) throws SAXException {
//endElement(String uri, String localName, String qName)
super.endElement(arg0, arg1, arg2);
System.out.println("MySaxHandler.endElement()!元素 "+arg2+" 节点闭合");
}
|
public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
//characters(char[] ch, int start, int length)
if(2==count){ //当前是第二本书的时候
String content = new String(arg0, arg1, arg2);
System.out.println("MySaxHandler.characters()!文本节点" + content);
count=-1; //修改变量,不然后面的都会输出
}
super.characters(arg0, arg1, arg2);
/*String content = new String(arg0, arg1, arg2);
System.out.println("MySaxHandler.characters()!文本节点" + content);*/
}
}
|
//第二种方法
SAX方式解析XML文档:
1、使用SAXParserFactory创建SAX解析工厂
SAXParserFactory spf = SAXParserFactory.newInstance();
2、通过SAX解析工厂得到解析器对象
SAXParser sp = spf.newSAXParser();
3、通过解析器对象得到一个XML的读取器
XMLReader xmlReader = sp.getXMLReader();
4、设置读取器的事件处理器
xmlReader.setContentHandler(new BookParserHandler());
5、解析xml文件
xmlReader.parse("book.xml");
|
|
cd.xml | sax.xml |
<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
<CD>
<TITLE>Greatest Hits</TITLE>
<ARTIST>Dolly Parton</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>RCA</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1982</YEAR>
</CD>
<CD>
<TITLE>Still got the blues</TITLE>
<ARTIST>Gary Moore</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Virgin records</COMPANY>
<PRICE>10.20</PRICE>
<YEAR>1990</YEAR>
</CD>
<CD>
<TITLE>Eros</TITLE>
<ARTIST>Eros Ramazzotti</ARTIST>
<COUNTRY>EU</COUNTRY>
<COMPANY>BMG</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1997</YEAR>
</CD>
<CD>
<TITLE>One night only</TITLE>
<ARTIST>Bee Gees</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Polydor</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1998</YEAR>
</CD>
</CATALOG>
程序结果: |
//导入包什么的没有放入笔记
public class sax {
public static void main(String[] args) throws Exception{
//使用SAXParserFactory创建SAX解析工厂
SAXParserFactory spf=SAXParserFactory.newInstance();
//通过SAX解析工厂得到解析器对象
SAXParser sp=spf.newSAXParser();
//通过解析器对象得到一个XML的读取器
XMLReader reader=sp.getXMLReader();
//设置读取器的事件处理器
MySaxhandler mySaxHandler=new MySaxhandler();
reader.setContentHandler(mySaxHandler);
//解析xml文件
reader.parse("cd.xml");
}
}
class MySaxhandler extends DefaultHandler{
String tagName;
public int i=0;
public int j=0;
public String[] artist = new String[6]; //记录所有ARTIST
public String[] country = new String[6]; //记录所有COUNTRY
public Boolean ta=false; //标记artist是读到元素开始还是元素结束
public Boolean tc=false; //标记country是读到元素开始还是元素结束
public void startElement(String uri, String localName,String qName, Attributes attributes) throws SAXException {
tagName=qName; //将读取到的标签名赋给tagName
if(tagName.equals("ARTIST")){
ta=true; //标记ARTIST是开始
}
if(tagName.equals("COUNTRY")){
tc=true; //标记ARTIST是结束
}
}
public void characters(char[] ch, int start, int length)throws SAXException {
String name=new String(ch, start, length); //拼接出每次的文本
if(tagName.equals("ARTIST")&& ta==true){ //如果读到ARTIST的开始,那么后面的文本就是我们要的,要存起来
ta=false; //记录完,后面读到的肯定是元素结束,这里事先标记
artist[i++]=name; //记录ARTIST
}
if(tagName.equals("COUNTRY")&&tc==true){
tc=false;
country[j++]=name;
}
}
public void endElement(String uri, String localName, String qName)throws SAXException {
tagName=qName;
if(tagName.equals("ARTIST")){
ta=false; //再次赋值为0,确保正确
}
if(tagName.equals("COUNTRY")){
tc=false;
}
}
public void endDocument() throws SAXException {
for(int k=0;k<j;k++){
if(country[k].equals("UK")){
System.out.println("MySaxDemo.main characters() "+artist[k]);
}
}
}
}
|