XML解析之使用Jaxp对XML进行DOM解析


1.XML的解析方式对比:

我们写好XML文档后,需要对XML文档进行CRUD,这就涉及如何对XML文档进行解析.

•dom:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种方式。

•sax: (Simple API for XML) 不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它。

  DOM解析 SAX解析   
优点 DOM将文档中的元素封装成对象 ,在内存中以树的结构存放,这样节点与节点之间有关系, 便于CRUD(create read update delete) 读一行解析一行,内存消耗相对较小,适合处理大型的XML文档
缺点 如果文档比较大(>1GB)那么再把文档中的元素封装成DOM对象可能导致内存溢出 不能CUD,如果想考虑DOM

XML解析器

•Crimson、Xerces 、Aelfred2

XML解析开发包

•Jaxp、Jdom、dom4j

2.Jaxp对XML进行DOM解析:

JAXP 开发包是J2SE的一部分,它由javax.xml、org.w3c.dom 、org.xml.sax 包及其子包组成

在 javax.xml.parsers 包中,定义了几个工厂类,程序员调用这些工厂类,可以得到对xml文档进行解析的 DOM 或 SAX 的解析器对象。

例如:book.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE 书架 SYSTEM "book.dtd">
<书架>
<>
    <书名>JavaWeb</书名>
    <作者 姓名="王五"/>
    <售价>50元</售价>
    <页面作者 个人爱好="上网" 网站职务="页面作者" 联系信息=""/>
 </>
</书架>
<!--
1. #REQUIRED :网站职务="设计",页面作者元素中一旦写了网站职务属性,那么该属性的值只能写 "页面作者"
否则报错: Attribute "网站职务" with value "设计" must have a value of "页面作者"
2.对于属性个人爱好的属性值,你写了,其属性值可以任意更改,但是不写默认为 "上网"
 -->

利用程序遍历内存中的dom树:(该思想和利用JS遍历HTML在内存的DOM树一致):

package itheima;
 
import java.io.File;
import java.io.IOException;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
 
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
 
public class JaxpParseXml {
    private String allNodesStr="";
    @Test
    public  void jaxpTest() throws ParserConfigurationException, SAXException, IOException{
        //获取工厂实例
       DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
        
       //获取DOM解析器
       DocumentBuilder db=dbf.newDocumentBuilder();
      
       //获取当前XML文档的dom实例
       Document dom=db.parse(new File("book.xml"));
      
       //打印这棵DOM树
    printDOM(dom,0);
    System.out.println(allNodesStr);
    
    //该方法主要是看书架的子节点#text的value值:\n
     textNodeTest(dom);
    } 
   public void printDOM(Node root,int level){
    printFormat(root,level);// 如果不加这句话在下面调用printDOM(nextNode,level);方法的节点不能被打印
    Node nextNode=null;  
    NodeList childNodes=null;
    if(root.hasChildNodes()){
         childNodes=root.getChildNodes();
        ++level;//每递归一次,级数+1
         for(int i=0;i<childNodes.getLength();++i){
             nextNode=childNodes.item(i); 
             if(nextNode.hasChildNodes())//该节点是否有子节点
                 printDOM(nextNode,level);
             else
                 printFormat(nextNode,level);
         }
    }
   }
   public void printFormat(Node nextNode,int level){
        allNodesStr+=format(level)+nextNode.getNodeName()+"..."+nextNode.getNodeType()+"..."+
                nextNode.getNodeValue()+"\n";
       }
   public String format(int level){//凸显层级目录
       StringBuilder sb=new StringBuilder("|--");
       while((level--)!=0)
           sb.insert(0,'*');
       return sb.toString();
   }
   
   public void textNodeTest(Node node){
      char[] charArr=node.getChildNodes().item(1).getChildNodes()
               .item(0).getNodeValue().toCharArray();
      System.out.println(charArr.length);
       for(char ch : charArr)
           System.out.println((int)ch);//10->换行(\n)
   }
}
XMLDOM树
依照打印结果做出这颗DOM树:
DOM树
注意一些问题:
1.#document节点为文档树的根,文档节点.
2.这里有两个书架节点,注意其type不同,type=1代表XML文档中的<书架></书架>
  type=10代表<!DOCTYPE 书架 SYSTEM "book.dtd">
类型名                      说明
Node                        表示所有类型值的统一接口,IE 不支持
Document                    表示文档类型
Element                     表示元素节点类型
Text                        表示文本节点类型
Comment                     表示文档中的注释类型
CDATASection                表示CDATA 区域类型
DocumentType                表示文档声明类型
DocumentFragment            表示文档片段类型
Attr                        表示属性节点类型

 

常量名说明                 nodeType          值
    ELEMENT_NODE                  元素            1
    ATTRIBUTE_NODE                属性            2
    TEXT_NODE                     文本            3
    CDATA_SECTION_NODE            CDATA           4
    ENTITY_REFERENCE_NODE         实体参考        5
    ENTITY_NODE                   实体            6
    PROCESSING_INSTRUCETION_NODE  处理指令        7
    COMMENT_NODE                  注释            8
   DOCUMENT_NODE                 文档根           9
   DOCUMENT_TYPE_NODE            doctype          10
   DOCUMENT_FRAGMENT_NODE        文档片段         11
   NOTATION_NODE                 符号             12

3.注意到DOM树中没有属性节点关于这点:属性节点不是元素的子节点,它只是描述该元素节点的一些性质而已,属于元素节点结构内部的一部分

3.DOM解析对XML文档进行CRUD(以上的book.xml不引用外部的dtd文档,book.xml的encoing="GBK"): (和用JS编程对DHTML的DOM树操作思想一致)

a.对于获取xml文件的DOM以及将内存中的DOM写入硬盘中的xml文件,经常用到,把他们两个分别封装在两个方法中:

   public Document getDOM(String  xmlPath) throws ParserConfigurationException, SAXException, IOException{
          DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
          DocumentBuilder db=dbf.newDocumentBuilder();
          return db.parse(new File(xmlPath));//拿到book.xml的dom
      }
 
 
    //将内存中的DOM写入到xml文件中,需要用到
    //javax.xml.transform.TransFormer:将源DOM树转化为结果DOM树
    public void write2XML(Node dom,String path) throws TransformerFactoryConfigurationError, TransformerException, UnsupportedEncodingException, FileNotFoundException{
          //同获取Document实例思想一致
        Transformer trans=TransformerFactory.newInstance().newTransformer();
         trans.transform(new DOMSource(dom),
       new StreamResult(new OutputStreamWriter(new FileOutputStream(new File(path)),"GBK")));//相当于将内存中修改好的DOM树写入到book.xml中
                                                          
        //trans.transform(new DOMSource(dom), new StreamResult(new File(path)));
         /*默认插入节点的编码方式为UTF-8,上面是用转换流来指定charset,可以解决乱码    
        乱码原因:写入UTF-8字节,而XML文档以GBK解码导致 乱码 ,例如:"你好"被解析为"浣犲ソ"*/
         
         //还有一点:在新的book.xml中<!DOCTYPE>引用外部dtd文件的语句被消除?(暂时未找到解决方法)
    }    
 
 
 
b.读取XML中的内容
 
      /*2.获取书名元素(标签)中封装的内容*/
      @Test
      public void getXmlElements() throws ParserConfigurationException, SAXException, IOException{
         
          NodeList eleNode=getDOM("book.xml").getElementsByTagName("书名");//获取到书名元素(标签)
          System.out.println(eleNode.item(0).getTextContent());//JavaWeb//书名中虽然用了引用实体获取,但是也可以通过该方法拿到
       }
   
      /*3.获取页面作者元素中的属性*/
      @Test
      public void getXmlProperties() throws ParserConfigurationException, SAXException, IOException{
        /*  NamedNodeMap attrMap=getDOM("book.xml").getAttributes();
           System.out.println(attrMap);    //注意getDOM代表#document这个节点,其没有属性,因此为null.
       */           
          //方法一:
        NamedNodeMap attrNodes=getDOM("book.xml").getElementsByTagName("页面作者").item(0).getAttributes();
        for(int index=0;index<attrNodes.getLength();++index)
            System.out.println(attrNodes.item(index));//遍历出页面作者中所有的属性及属性值  //属性="属性值"
        System.out.println(attrNodes.getNamedItem("网站职务")+"\n");//网站职务="页面作者" //底层AttrImpl复写了toString方法
         
        //方法二:在知道是Element实例的情况下,强制向下转型(Node->Element),为了使用Element提供的获取属性的方法
         Element eleNode=(Element) (getDOM("book.xml").getElementsByTagName("页面作者").item(0));
         System.out.println(eleNode.getAttribute("网站职务"));//该方法以String形式直接返回属性的值
         System.out.println(eleNode.getAttributeNode("网站职务"));//该方法返回Attr,可以进一步使用Attr提供的方法
      }
      
    getAttri
c.修改DOM树中的节点:
    /*4.修改DOM树中的节点*/
    @Test
    public void updateNode() throws DOMException, ParserConfigurationException, SAXException, IOException, TransformerFactoryConfigurationError, TransformerException{
           //修该售价标签中封装的内容为"50元"
        Document dom= getDOM("book.xml");
        dom.getElementsByTagName("售价").item(0).setTextContent("50元");//该语句执行完book.xml不会有任何变化
          //因为仅仅是修改了内存中的DOM树,而没有重新写入硬盘上的book.xml.
  
        
        write2XML(dom,"book.xml");          
    }

/*5.在原有的书标签之前再插入一个书标签*/

    @Test
    public void insertNode() throws ParserConfigurationException, SAXException, IOException, TransformerFactoryConfigurationError, TransformerException{
        Document dom= getDOM("book.xml");
        Node bookShelf=dom.getElementsByTagName("书架").item(0);
        Node refChild=dom.getElementsByTagName("书").item(0);
     
        Node bookNode=dom.createElement("书");
        bookShelf.insertBefore(bookNode, refChild);//会将newChild插入到refChild之前
        
        bookNode.appendChild(dom.createElement("你好"));//在书标签中添加一个子节点 书名
        write2XML(dom, "book.xml");
        /*
         犯的错误:
         使用dom.insertBefore(newChild,refChild) :DOMException - HIERARCHY_REQUEST_ERR
         API描述:如果此节点为不允许 newChild 节点类型的子节点的类型,也就是说调用该方法的节点不可能有该子节点
          
         */
    }
 
原XML文档:
<?xml version="1.0" encoding="GBK" standalone="yes"?>
<书架>
<>
    <书名>JavaWeb</书名>
    <作者 姓名="王五"/>
    <售价>50元</售价>
    <页面作者 个人爱好="上网" 网站职务="页面作者" 联系信息=""/>
 </>
</书架>
执行完上面插入节点代码后:
<?xml version="1.0" encoding="GBK"?><书架>
<><你好/></><>
    <书名>JavaWeb</书名>
    <作者 姓名="王五"/>
    <售价>50元</售价>
    <页面作者 个人爱好="上网" 网站职务="页面作者" 联系信息=""/>
 </>
</书架>
/*删除节点*/
    @Test
    public void deleteNode() throws ParserConfigurationException, SAXException, IOException, TransformerFactoryConfigurationError, TransformerException{
        Document dom=getDOM("book.xml");
        Node delNode=dom.getElementsByTagName("页面作者").item(0);
       Node book=dom.getElementsByTagName("书").item(0);
       book.removeChild(delNode);
       write2XML(dom,"book.xml");
       //删除一个节点,那么该节点的所有子节点将不存在
    }
posted @ 2014-02-04 21:39  伊秋  阅读(738)  评论(0编辑  收藏  举报