zoukankan      html  css  js  c++  java
  • 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值:
    
         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()+"
    ";
           }
       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->换行(
    )
       }
    }
    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("网站职务")+"
    ");//网站职务="页面作者" //底层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");
           //删除一个节点,那么该节点的所有子节点将不存在
        }
  • 相关阅读:
    创建HttpFilter与理解多个Filter代码的执行顺序
    Filter
    JSTL
    EL
    JavaBean
    HttpSession之表单的重复提交 & 验证码
    相对路径和绝对路径
    HttpSession之简易购物车
    HttpSession
    Cookie
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3538017.html
Copyright © 2011-2022 走看看