1. 程式人生 > >Dom解析和生成XML文件

Dom解析和生成XML文件

一、前言

    Dom即檔案物件模型(Document Object Model),是W3C組織推薦的使用可擴充套件標記性語言的標準介面, 它主要用於讀寫xml文件,使用起來還是非常不錯的。  另外Dom將整個xml檔案對映成一個有層次的節點的結構,分別1級、2級到多級,這樣子使整個繁瑣的文件數量大但是依然很清晰,然後就可以非常方便的方便讀寫xml文件了。

二、準備條件

   因為Dom是jdk自帶的解析方式,所以不用新增jar包引用。

三、使用Dom實戰

1、解析xml文件

實現思路:

   <1>根據讀取的xml路徑,傳遞給DocumentBuilder之後 返回一個Document文件物件;

   <2>然後操作這個Document物件,獲取它下面的節點以及子節點的資訊;

具體程式碼如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
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.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


/**
 * 使用dom解析和生成xml文件
 * @author Administrator
 *
 */
public class DomOperateXmlDemo {

	public void parseXml01(){
		try {
			//建立DocumentBuilder工廠例項
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			//new一個新的DocumentBuilder
			DocumentBuilder db = dbf.newDocumentBuilder();
			
			String xmlPath = "D:\\project\\dynamicWeb\\src\\resource\\server01.xml";
			String xmlName = xmlPath.substring(xmlPath.lastIndexOf("\\"));
			//解析xml轉換為document文件物件,該方法支援過載
			//1、直接指定xml的絕對路徑可以完成
			//Document document = db.parse(xmlPath);
			//2、使用InputStream輸入流讀取xml檔案,然後把這個輸入流傳遞進去
			InputStream inputStream = this.getClass().getResourceAsStream(xmlName);
			//3、也可以指定路徑完成InputStream輸入流的例項化操作
			//InputStream inputStream = new FileInputStream(new File(xmlPath));
			//4、使用InputSource輸入源作為引數也可以轉換xml
			//InputSource inputSource = new InputSource(inputStream);
			//Document document = db.parse(inputSource);
			Document document = db.parse(inputStream);
			//獲取當前物件的子節點列表,返回的是一個根節點集合
			NodeList nodeList = document.getChildNodes();
			//獲取根節點可以用NodeList集合返回它的第一個元素,並且它的型別是org.w3c.dom.Node的
			//Node rootNode = nodeList.item(0);
			Element rootNode = document.getDocumentElement();  
			//上面的返回型別是Element,也可以使用 Node接收,因為Element介面繼承Node介面,使用Node只不過方法沒有Element多,可以自己嘗試一下就知道了
			System.out.println("根節點的節點名稱:" + rootNode.getNodeName());
			System.out.println("根節點的標籤名稱:" + rootNode.getTagName());
			System.out.println("根節點的節點型別:" + rootNode.getNodeType());
			System.out.println("根節點的節點值:" + rootNode.getFirstChild().getNodeValue());//rootNode.getNodeValue();返回的一直是null,因為程式不知道你到底要獲取的哪個節點的value,所以只能獲取子節點的value
			System.out.println("根節點的節點文字內容:" + rootNode.getTextContent());//返回當前節點下所有子標籤的文字內容,並且換行是因為xml中有換行符
			System.out.println("根節點的指定屬性的值:" + rootNode.getAttribute("port")); 
			//直接獲取所有屬性的集合
			NamedNodeMap nameNodeMap = rootNode.getAttributes();
			for (int i = 0; i < nameNodeMap.getLength(); i++) {
				System.out.println("根節點屬性" + nameNodeMap.item(i).getNodeName() + ":" + rootNode.getAttribute(nameNodeMap.item(i).getNodeName()));
			}
			//獲取根節點的子節點集合資訊
			NodeList subNodeList = rootNode.getChildNodes(); 
			for (int i = 0; i < subNodeList.getLength(); i++) {
				System.out.println("子節點的節點名稱:" + subNodeList.item(i).getNodeName());
				System.out.println("子節點的節點文字內容:" + subNodeList.item(i).getTextContent());
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		DomOperateXmlDemo demo = new DomOperateXmlDemo();
		demo.parseXml01();
	}
}
上面程式碼簡單解析了一個xml,server01.xml檔案的內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" id="server">
	Server標籤內的內容
	<ServerName>服務名稱內容</ServerName>
</Server>
接下來執行該類的main方法,console效果如下:

由此可知:

   <1>根據控制檯顯示可知,根節點獲取的子節點集合也包含文字內容,返回的標籤節點名稱是#text這樣;
   <2>另外getTextContent()獲取的是該節點下所有的子節點的文字資訊,而getNodeValue()只是獲取當前節點的文字資訊,如果是標籤則返回null。

上面只是簡單的獲取了xml的根目錄的元素,接下來使用Document自帶的方法檢索節點以及修改節點內的內容。

具體程式碼如下:

	public void parseXml02(){
		try{
			//建立DocumentBuilder工廠例項
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			//new一個新的DocumentBuilder
			DocumentBuilder db = dbf.newDocumentBuilder();
			InputStream inputStream = this.getClass().getResourceAsStream("server02.xml");
	
			Document document = db.parse(inputStream);
			//根據節點名稱獲取節點集合
			NodeList nodeList = document.getElementsByTagName("Service");
			for (int i = 0; i < nodeList.getLength(); i++) {
				System.out.println("節點" + nodeList.item(i).getNodeName() + ":" + nodeList.item(i).getTextContent() + ",它的" 
						+ nodeList.item(i).getAttributes().item(0).getNodeName() + "屬性值是:" + nodeList.item(i).getAttributes().item(0).getTextContent());
			}
			
			Element element = document.getElementById("server"); //不知道為什麼總是返回null,費解
			System.out.println(element);
			
			//修改子元素的標籤名稱以及標籤內容
			Node node = nodeList.item(0);
			node.getFirstChild().setNodeValue("這個是修改之後的內容..");
			//輸出一下檢視效果
			System.out.println("修改之後的Node的nodeValue:" + node.getFirstChild().getNodeValue());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
另外上面的xml在src下面,server02.xml具體如下:
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN" id="server">
	<Service name="Catalina" id="aa">第一個服務配置</Service>
  	<Service name="Catalina01">第二個服務配置</Service>
</Server>
接下來執行該類的main方法,console效果如下:

由此可知:

   <1>通過node聯想出來的方法發現可以修改的屬性確實有點少,並且獲取標籤的屬性方法太繁瑣了。

但是上面只是簡單的獲取了子節點元素,但是如果xml規則比較複雜,比如接下來要測試的server03.xml,具體如下:

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
	<Service name="Catalina">
	    <Connector>第一個聯結器</Connector>
		<Connector>第二個聯結器
			<open>開啟服務</open>
			<init>初始化一下</init>
			<service>執行請求服務</service>
			<destory>銷燬一下</destory>
			<close>關閉服務</close>
		</Connector>
  	</Service>
</Server>
具體實現程式碼如下:
	public void parseXml03(){
		try{
			//建立DocumentBuilder工廠例項
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			//new一個新的DocumentBuilder
			DocumentBuilder db = dbf.newDocumentBuilder();
			InputStream inputStream = this.getClass().getResourceAsStream("server03.xml");
	
			Document document = db.parse(inputStream);
			//根據節點名稱獲取節點集合
			NodeList nodeList = document.getDocumentElement().getChildNodes();
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				if(!"#text".equals(node.getNodeName())){
					System.out.println("【1】" + node.getNodeName() + ":" + node.getFirstChild().getNodeValue());
				}
				NodeList subNodeList = node.getChildNodes();
				for (int j = 0; j < subNodeList.getLength(); j++) {
					Node subNode = subNodeList.item(j);
					if(!"#text".equals(subNode.getNodeName())){
						System.out.println("      【2】" + subNode.getNodeName() + ":" + subNode.getFirstChild().getNodeValue());
					}
					NodeList subSubNodeList = subNode.getChildNodes();
					for (int k = 0; k < subSubNodeList.getLength(); k++) {
						Node subSubNode = subSubNodeList.item(k);
						if(!"#text".equals(subSubNode.getNodeName())){
							System.out.println("            【3】" + subSubNode.getNodeName() + ":" + subSubNode.getFirstChild().getNodeValue());
						}
					}
				}
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

接下來執行該類的main方法,console效果如下:


由此可知:

   <1>實踐發現 如果標籤內有標籤,就不能使用node.getFirstChild().getNodeValue(),會報錯  Exception in thread "main" java.lang.NullPointerException
   <2>因為標籤與標籤之前的字串也算是節點,然後獲取node.getFirstChild()肯定返回空,所以可以使用!"#text".equals(node.getNodeName())過濾掉不是標籤內容的節點。
   <3>根據上面的程式碼可知 dom取值非常的不方便,很容易空引用,並且每次讀取都要全部載入,萬一檔案很大,就全部都要執行在記憶體之中,容易造成記憶體溢位。
   

2、生成xml文件

dom能夠解析xml,同樣肯定能生成xml,而且使用起來更加簡單方便。

實現思路:

   <1>DocumentBuilder提供了建立Document物件的方法;

   <2>操作這個Document物件,新增節點以及節點下的文字、名稱和屬性值;

   <3>然後利用PrintWriter寫入器把封裝的document物件寫入到磁碟中;

具體程式碼如下:
    public void buildXml01(){
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            //建立一個新的document文件物件,這裡返回的document是 org.w3c.dom.Document
            Document document = db.newDocument();
            //document.setXmlVersion("UTF-8");//預設UTF-8
            Element root = document.createElement("root");
            root.setTextContent("根節點內容");
            root.setAttribute("attr", "nothing");
            document.appendChild(root);//這一步必不可少,繫結父子標籤的關聯關係
            
            //TransformerFactory這個工廠專門生產Transformer的例項,Transformer例項就可以把封裝好的document變成xml格式的文件了
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer transformer = tf.newTransformer();
            DOMSource source = new DOMSource(document);
            
            //檔案寫入器
            PrintWriter printWriter = new PrintWriter(new FileOutputStream("c:\\server.xml"));
            StreamResult result = new StreamResult(printWriter);
            //執行寫入操作
            transformer.transform(source, result);
            System.out.println("生成xml檔案成功");
            printWriter.close();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
    }
接下來執行該方法,console顯示執行成功,然後開啟生成的xml檔案:


結果顯示 與自己期望的內容一樣。