XML:extensiable markup language 被稱做可擴展標記語言css
XML簡單的歷史介紹:html
XML文件就解決了以上的問題了,若是使用XML描述上述圖片的關係,是很是簡單的!node
<?xml version="1.0" encoding="UTF-8" ?> <中國> <北京> <海淀></海淀> <豐臺></豐臺> </北京> <湖南> <長沙></長沙> <岳陽></岳陽> </湖南> <湖北> <武漢></武漢> <荊州></荊州> </湖北> </中國>
XML文件還能使用瀏覽器打開:程序員
咱們能夠發現XML是能夠描述很複雜的數據關係的web
①:配置文件(例子:Tomcat的web.xml,server.xml......),XML可以很是清晰描述出程序之間的關係數據庫
②:程序間數據的傳輸,XML的格式是通用的,可以減小交換數據時的複雜性!瀏覽器
③:充當小型數據庫,若是咱們的數據有時候須要人工配置的,那麼XML充當小型的數據庫是個不錯的選擇,程序直接讀取XML文件顯然要比讀取數據庫要快呢!微信
XML被設計爲「什麼都不作」,XML數據或XML文檔只用於組織、存儲數據,除此以外的數據生成、讀取、傳送、存取等等操做都與XML自己無關!架構
因而乎,想要操做XML,就須要用到XML以外的技術了:app
XML聲明放在XML的第一行
version----版本
encoding--編碼
standalone--獨立使用--默認是no。standalone表示該xml是否是獨立的,若是是yes,則表示這個XML文檔時獨立的,不能引用外部的DTD規範文件;若是是no,則該XML文檔不是獨立的,表示能夠引用外部的DTD規範文檔。
正確的文檔聲明格式,屬性的位置不能改變!
<?xml version="1.0" encoding="utf-8" standalone="no"?>
首先在這裏說明一個概念:在XML中元素和標籤指的是同一個東西!不要被不一樣的名稱所迷惑了!
元素中須要值得注意的地方:
看起來好像有不少須要值得注意的地方,其實只須要記住:XML的語法是規範的!不要隨意亂寫!
屬性是做爲XML元素中的一部分的,命名規範也是和XML元素同樣的!
<!--屬性名是name,屬性值是china--> <中國 name="china"> </中國>
註釋和HTML的註釋是同樣的
<!---->
在編寫XML文件時,有些內容可能不想讓解析引擎解析執行,而是看成原始內容處理。遇到此種狀況,能夠把這些內容放在CDATA區裏,對於CDATA區域內的內容,XML解析程序不會處理,而是直接原封不動的輸出
語法:
<![CDATA[ ...內容 ]]>
對於一些單個字符,若想顯示其原始樣式,也可使用轉義的形式予以處理。
處理指令,簡稱PI (processing instruction)。處理指令用來指揮解析引擎如何解析XML文檔內容。
例如:
在XML文檔中可使用xml-stylesheet指令,通知XML解析引擎,應用css文件顯示xml文檔內容。
<?xml-stylesheet type="text/css" href="1.css"?>
<?xml version="1.0" encoding="UTF-8" ?> <?xml-stylesheet type="text/css" href="1.css"?> <china> <guangzhou> 廣州 </guangzhou> <shenzhen> 深圳 </shenzhen> </china>
guangzhou{ font-size: 40px; }
①:JAXP(The Java API For XML Processing):主要負責解析XML
②:JAXB(Java Architecture for XML Binding):主要負責將XML映射爲Java對象
前面XML章節已經說了,XML被設計爲「什麼都不作」,XML只用於組織、存儲數據,除此以外的數據生成、讀取、傳送等等的操做都與XML自己無關!
XML解析就是讀取XML的數據!
XML解析方式分爲兩種:
①:dom(Document Object Model)文檔對象模型,是W3C組織推薦解析XML的一種方式
②:sax(Simple API For XML),它是XML社區的標準,幾乎全部XML解析器都支持它!
從上面的圖很容易發現,應用程序不是直接對XML文檔進行操做的,而是由XML解析器對XML文檔進行分析,而後應用程序經過XML解析器所提供的DOM接口或者SAX接口對分析結果進行操做,從而間接地實現了對XML文檔的訪問!
經常使用的解析器和解析開發包的關係以下所示:
雖然jaxp解析XML的性能以及開發的簡易度是沒有dom4j好,可是jaxp無論怎麼說都是JDK內置的開發包,咱們是須要學習的!
DOM解析是一個基於對象的API,它把XML的內容加載到內存中,生成與XML文檔內容對應的模型!當解析完成,內存中會生成與XML文檔的結構與之對應的DOM對象樹,這樣就可以根據樹的結構,以節點的形式對文檔進行操做!
簡單來講:DOM解析會把XML文檔加載到內存中,生成DOM樹的元素都是以對象的形式存在的!咱們操做這些對象就可以操做XML文檔了!
既然XML文檔的數據是帶有關係型的,那麼生成的DOM樹的節點也是有關係的:
在DOM解析中有幾個核心的操做接口:
節點之間的關係圖:
好的,不跟大家多bb,咱們來使用一下Dom的方式解析XML文檔吧!
<?xml version="1.0" encoding="UTF-8" ?> <china> <guangzhou >廣州</guangzhou> <shenzhen>深圳</shenzhen> <beijing>北京</beijing> <shanghai>上海</shanghai> </china>
public class DomParse { public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException { //API規範:須要用一個工廠來造解析器對象,因而我先造了一個工廠! DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //獲取解析器對象 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); //獲取到解析XML文檔的流對象 InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml"); //解析XML文檔,獲得了表明XML文檔的Document對象! Document document = documentBuilder.parse(inputStream); } }
可能咱們會有兩種想法:
咱們來對XML文檔遍歷一下吧,爲了更好地重用,就將它寫成一個方法吧(也是可以更好地用遞歸實現功能)!
public class DomParse { public static void main(String[] args) throws ParserConfigurationException, IOException, SAXException { //API規範:須要用一個工廠來造解析器對象,因而我先造了一個工廠! DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); //獲取解析器對象 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); //獲取到解析XML文檔的File對象 InputStream inputStream = DomParse.class.getClassLoader().getResourceAsStream("city.xml"); //解析XML文檔,獲得了表明XML文檔的Document對象! Document document = documentBuilder.parse(inputStream); //把表明XML文檔的document對象傳遞進去給list方法 list(document); } //咱們這裏就接收Node類型的實例對象吧!多態!!! private static void list(Node node) { //判斷是不是元素節點,若是是元素節點就直接輸出 if (node.getNodeType() == Node.ELEMENT_NODE) { System.out.println(node.getNodeName()); } //....若是沒有進入if語句,下面的確定就不是元素節點了,因此獲取到子節點集合 NodeList nodeList = node.getChildNodes(); //遍歷子節點集合 for (int i = 0; i < nodeList.getLength(); i++) { //獲取到其中的一個子節點 Node child = nodeList.item(i); //...判斷該子節點是否爲元素節點,若是是元素節點就輸出,不是元素節點就再獲取到它的子節點集合...遞歸了 list(child); } } }
如今我要作的就是:讀取guangzhou這個節點的文本內容!
private static void read(Document document) { //獲取到全部名稱爲guangzhou節點 NodeList nodeList = document.getElementsByTagName("guangzhou"); //取出第一個名稱爲guangzhou的節點 Node node = nodeList.item(0); //獲取到節點的文本內容 String value = node.getTextContent(); System.out.println(value); }
如今我想多增長一個城市節點(杭州),我須要這樣作:
private static void add(Document document) { //建立須要增長的節點 Element element = document.createElement("hangzhou"); //向節點添加文本內容 element.setTextContent("杭州"); //獲得須要添加節點的父節點 Node parent = document.getElementsByTagName("china").item(0); //把須要增長的節點掛在父節點下面去 parent.appendChild(element); }
作到這裏,我僅僅在內存的Dom樹下添加了一個節點,要想把內存中的Dom樹寫到硬盤文件中,須要轉換器!
獲取轉換器也十分簡單:
//獲取一個轉換器它須要工廠來造,那麼我就造一個工廠 TransformerFactory transformerFactory = TransformerFactory.newInstance(); //獲取轉換器對象 Transformer transformer = transformerFactory.newTransformer();
它須要一個Source實例對象和Result的實例對象,這兩個接口究竟是什麼玩意啊?
因而乎,我就去查API,發現DomSource實現了Source接口,咱們使用的不正是Dom解析嗎,再看看構造方法,感受就是它了!
而SteamResult實現了Result接口,有人也會想,DomResult也實現了Result接口啊,爲何不用DomResult呢?咱們如今作的是把內存中的Dom樹更新到硬盤文件中呀,固然用的是StreamResult啦!
完整代碼以下:
private static void add(Document document) throws TransformerException { //建立須要增長的節點 Element element = document.createElement("hangzhou"); //向節點添加文本內容 element.setTextContent("杭州"); //獲得須要添加節點的父節點 Node parent = document.getElementsByTagName("china").item(0); //把須要增長的節點掛在父節點下面去 parent.appendChild(element); //獲取一個轉換器它須要工廠來造,那麼我就造一個工廠 TransformerFactory transformerFactory = TransformerFactory.newInstance(); //獲取轉換器對象 Transformer transformer = transformerFactory.newTransformer(); //把內存中的Dom樹更新到硬盤中 transformer.transform(new DOMSource(document),new StreamResult("city.xml")); }
剛剛增長的節點是在china節點的末尾處的,如今我想指定增長節點的在beijing節點以前,是這樣作的:
private static void add2(Document document) throws TransformerException { //獲取到beijing節點 Node beijing = document.getElementsByTagName("beijing").item(0); //建立新的節點 Element element = document.createElement("guangxi"); //設置節點的文本內容 element.setTextContent("廣西"); //獲取到要建立節點的父節點, Node parent = document.getElementsByTagName("china").item(0); //將guangxi節點插入到beijing節點以前! parent.insertBefore(element, beijing); //將內存中的Dom樹更新到硬盤文件中 TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(new DOMSource(document), new StreamResult("city.xml")); }
如今我要刪除的是beijing這個節點!
private static void delete(Document document) throws TransformerException { //獲取到beijing這個節點 Node node = document.getElementsByTagName("beijing").item(0); //獲取到父節點,而後經過父節點把本身刪除了 node.getParentNode().removeChild(node); //把內存中的Dom樹更新到硬盤文件中 TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(new DOMSource(document), new StreamResult("city.xml")); }
將guangzhou節點的文本內容修改爲廣州你好
private static void update(Document document) throws TransformerException { //獲取到guangzhou節點 Node node = document.getElementsByTagName("guangzhou").item(0); node.setTextContent("廣州你好"); //將內存中的Dom樹更新到硬盤文件中 TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(new DOMSource(document), new StreamResult("city.xml")); }
XML文檔是可能帶有屬性值的,如今咱們要guangzhou節點上的屬性
private static void updateAttribute(Document document) throws TransformerException { //獲取到guangzhou節點 Node node = document.getElementsByTagName("guangzhou").item(0); //如今node節點沒有增長屬性的方法,因此我就要找它的子類---Element Element guangzhou = (Element) node; //設置一個屬性,若是存在則修改,不存在則建立! guangzhou.setAttribute("play", "gzchanglong"); //若是要刪除屬性就用removeAttribute()方法 //將內存中的Dom樹更新到硬盤文件中 TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(new DOMSource(document), new StreamResult("city.xml")); }
SAX採用的是一種順序的模式進行訪問,是一種快速讀取XML數據的方式。當時候SAX解析器進行操做時,會觸發一系列事件SAX。採用事件處理的方式解析XML文件,利用 SAX 解析 XML 文檔,涉及兩個部分:解析器和事件處理器
sax是一種推式的機制,你建立一個sax 解析器,解析器在發現xml文檔中的內容時就告訴你(把事件推給你). 如何處理這些內容,由程序員本身決定。
當解析器解析到<?xml version="1.0" encoding="UTF-8" standalone="no"?>
聲明頭時,會觸發事件。解析到<china>
元素頭時也會觸發事件!也就是說:當使用SAX解析器掃描XML文檔(也就是Document對象)開始、結束,以及元素的開始、結束時都會觸發事件,根據不一樣事件調用相對應的方法!
首先咱們仍是先拿到SAX的解析器再說吧!
//要獲得解析器對象就須要造一個工廠,因而我造了一個工廠 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); //獲取到解析器對象 SAXParser saxParse = saxParserFactory.newSAXParser();
@Override public void startDocument() throws SAXException { super.startDocument(); } @Override public void endDocument() throws SAXException { super.endDocument(); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); } @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); }
public static void main(String[] args) throws Exception{ //要獲得解析器對象就須要造一個工廠,因而我造了一個工廠 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); //獲取到解析器對象 SAXParser saxParse = saxParserFactory.newSAXParser(); //獲取到XML文檔的流對象 InputStream inputStream = SAXParse.class.getClassLoader().getResourceAsStream("city.xml"); saxParse.parse(inputStream, new MyHandler()); }
public class MyHandler extends DefaultHandler { @Override public void startDocument() throws SAXException { System.out.println("我開始來掃描啦!!!!"); } @Override public void endDocument() throws SAXException { System.out.println("我結束了!!!!"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //若是要解析出節點屬性的內容,也很是簡單,只要經過attributes變量就好了! //輸出節點的名字! System.out.println(qName); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println(qName); } @Override public void characters(char[] ch, int start, int length) throws SAXException { System.out.println(new String(ch,start,length)); } }
咱們發現,事件處理器的代碼都很是簡單,而後就如此簡單地就可以遍歷整個XML文檔了!
若是要查詢單獨的某個節點的內容也是很是簡單的喲!只要在startElement()方法中判斷名字是否相同便可!
如今我只想查詢guangzhou節點的內容:
//定義一個標識量,用於指定查詢某個節點的內容 boolean flag = false; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //若是節點名稱是guangzhou,我才輸出,而且把標識量設置爲true if (qName == "guangzhou") { System.out.println(qName); flag = true; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { //只有在flag爲true的狀況下我才輸出文本的內容 if (flag == true) { System.out.println(new String(ch, start, length)); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //在執行到元素的末尾時,不要忘了將標識量改爲false if (qName == "guangzhou" && flag == true) { System.out.println(qName); flag = false; } }
DOM解析讀取整個XML文檔,在內存中造成DOM樹,很方便地對XML文檔的內容進行增刪改。但若是XML文檔的內容過大,那麼就會致使內存溢出!
SAX解析採用部分讀取的方式,能夠處理大型文件,但只能對文件按順序從頭至尾解析一遍,不支持文件的增刪改操做
DOM和SAX解析有着明顯的差異,何時使用DOM或者SAX就很是明瞭了。
Dom4j是一個很是優秀的Java XML API,具備性能優異、功能強大和極易使用的特色。
dom缺點:比較耗費內存
sax缺點:只能對xml文件進行讀取,不能修改,添加,刪除
dom4j:既能夠提升效率,同時也能夠進行crud操做
由於dom4j不是sun公司的產品,因此咱們開發dom4j須要導入開發包
//獲取到解析器 SAXReader saxReader = new SAXReader(); //獲取到XML文件的流對象 InputStream inputStream = DOM4j.class.getClassLoader().getResourceAsStream("1.xml"); //經過解析器讀取XML文件 Document document = saxReader.read(inputStream);
咱們都知道,Document表明的是XML文檔,通常咱們都是經過Document對象開始,來進行CRUD(增刪改查)操做的!
獲取Document對象有三種方式:
①:讀取XML文件,得到document對象(這種最經常使用)
SAXReader reader = new SAXReader(); Document document = reader.read(new File("input.xml"));
②:解析XML形式的文本,獲得document對象
String text = "<members></members>"; Document document=DocumentHelper.parseText(text);
③:主動建立document對象.
Document document =DocumentHelper.createDocument(); //建立根節點 Element root = document.addElement("members");
讀取XML文檔的數據,都是經過Document獲取根元素,再經過根元素獲取獲得其餘節點的,從而進行操做!
若是XML的結構有多層,須要一層一層地獲取!
@Test public void read() throws DocumentException { //獲取到解析器 SAXReader saxReader = new SAXReader(); //獲取到XML文件的流對象 InputStream inputStream = dom4j11.class.getClassLoader().getResourceAsStream("1.xml"); //經過解析器讀取XML文件 Document document = saxReader.read(inputStream); //獲取獲得根節點 Element root = document.getRootElement(); //獲取獲得name節點 Element name = root.element("name"); //獲得了name節點,就能夠獲取name節點的屬性或者文本內容了! String text = name.getText(); String attribute = name.attributeValue("littleName"); System.out.println("文本內容是:" + text); System.out.println("屬性內容是:" + attribute); }
<?xml version="1.0" encoding="UTF-8" ?> <person> <name littleName="fucheng">zhongfucheng</name> <age>20</age> </person>
//獲取獲得根節點 Element root = document.getRootElement(); //一層一層地獲取到節點 Element element = root.element("guangdong").element("guangzhou").element("luogang"); String value = element.getText(); System.out.println(value);
在DOM4j中要對內存中的DOM樹寫到硬盤文件中,也是要有轉換器的支持的!
dom4j提供了XMLWriter供咱們對XML文檔進行更新操做,通常地建立XMLWriter的時候咱們都會給出兩個參數,一個是Writer,一個是OutputFormat
這個OutputFormat有什麼用的呢?其實就是指定回寫XML的格式和編碼格式。細心的朋友會發現,上面咱們在jaxp包下使用dom解析的Transformer類,把內存中的DOM樹更新到文件硬盤中,是沒有格式的!不信倒回去看看!這個OutputFormat就可讓咱們更新XML文檔時也能帶有格式!
//建立帶有格式的對象 OutputFormat outputFormat = OutputFormat.createPrettyPrint(); //設置編碼,默認的編碼是gb2312,讀寫的編碼不一致,會致使亂碼的! outputFormat.setEncoding("UTF-8"); //建立XMLWriter對象 XMLWriter xmlWriter = new XMLWriter(new FileWriter("2.xml"), outputFormat); //XMLWriter對象寫入的是document xmlWriter.write(document); //關閉流 xmlWriter.close();
@Test public void add() throws Exception { //獲取到解析器 SAXReader saxReader = new SAXReader(); //獲取到XML文件的流對象 InputStream inputStream = dom4j11.class.getClassLoader().getResourceAsStream("1.xml"); //經過解析器讀取XML文件 Document document = saxReader.read(inputStream); //建立出新的節點,爲節點設置文本內容 Element newElement = DocumentHelper.createElement("name"); newElement.setText("ouzicheng"); //獲取到根元素 Element root = document.getRootElement(); //把新建立的name節點掛在根節點下面 root.add(newElement); //建立帶有格式的對象 OutputFormat outputFormat = OutputFormat.createPrettyPrint(); //設置編碼,默認的編碼是gb2312,讀寫的編碼不一致,會致使亂碼的! outputFormat.setEncoding("UTF-8"); //建立XMLWriter對象 XMLWriter xmlWriter = new XMLWriter(new FileWriter("2.xml"), outputFormat); //XMLWriter對象寫入的是document xmlWriter.write(document); //關閉流 xmlWriter.close(); }
在指定的位置增長節點!如今我想的就是在age屬性前面添加節點!
//建立一個新節點 Element element = DocumentHelper.createElement("name"); element.setText("ouzciheng"); //獲取獲得person下全部的節點元素! List list = document.getRootElement().elements(); //將節點添加到指定的位置上 list.add(1, element);
//獲取獲得age元素 Element age = document.getRootElement().element("age"); age.setText("9999");
//獲取獲得age節點 Element age = document.getRootElement().element("age"); //獲得age節點的父節點,使用父節點的remove刪除age節點! age.getParent().remove(age);
XPath 是一門在 XML 文檔中查找信息的語言。XPath 用於在 XML 文檔中經過元素和屬性進行導航。
上面咱們使用dom4j的時候,要獲取某個節點,都是經過根節點開始,一層一層地往下尋找,這就有些麻煩了!
若是咱們用到了XPATH這門語言,要獲取獲得XML的節點,就很是地方便了!
使用XPATH須要導入開發包jaxen-1.1-beta-7,咱們來看官方的文檔來入門吧。
//直接獲取到luogang節點 org.dom4j.Node node = document.selectSingleNode("//luogang"); //獲取節點的內容 String value = node.getText(); System.out.println(value);
獲取什麼類型的節點,XPATH的字符串應該怎麼匹配,查文檔就知道了,這裏就再也不贅述了。!
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗,能夠關注微信公衆號:Java3y