XML解析之JAXP

时间:2024-04-20 10:36:15

body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

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方法 ,这个方法会自动创建一个工厂的对象并返回。
XML解析之JAXP
获得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);
XML解析之JAXP
//打印某节点的所有元素节点 ,第二个节点
                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);
                        }
                }
XML解析之JAXP
//修改某个元素节点的主体内容,把第一本书的作者改成梅浩
                Node book1=bookList.item(0);
                book1.getChildNodes().item(3).setTextContent("梅浩");  //这里要写回XML才会生效
XML解析之JAXP
//向指定元素节点上增加同级元素节点
                Element newElement=document.createElement("销量"); //这里注意,新建的元素只有一个,添加完就没有了;比如给每个学生添加一个性别属性,用for循环添加,最只会把新建的节点添加到最后一个for循环满足添加的节点,然后写入xml
                newElement.setTextContent("1000");
                book1.appendChild(newElement);

XML解析之JAXP

//删除指定元素节点
                Node shoujianode=document.getElementsByTagName("售价").item(0);
                book1.removeChild(shoujianode);

XML解析之JAXP

//操作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);

XML解析之JAXP
//把内存中的dom树输出到磁盘中的文档的标准写法;只有增加、修改才会写入
                TransformerFactory tsf =TransformerFactory.newInstance();
                Transformer tsTransformer = tsf.newTransformer();
                tsTransformer.transform(
                new DOMSource(document),
                new StreamResult("src/Books.xml"));
        }
}//xml会重新保存在src下
XML解析之JAXP XML解析之JAXP
((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解析器解析到的数据,从而可以决定如何对数据进行处理。
XML解析之JAXP//事件处理器有一个缺省的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()!文档解析结束");
        }
XML解析之JAXP
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+"  节点闭合");
        }
XML解析之JAXP
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);*/
        }
}
XML解析之JAXP

//第二种方法
XML解析之JAXP
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>

程序结果:
XML解析之JAXP
//导入包什么的没有放入笔记
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]);
                        }
                }
        }
}