1. 程式人生 > >Dom4j解析帶有名稱空間的XML檔案

Dom4j解析帶有名稱空間的XML檔案

    今天我在解析KML檔案的過程中,使用XPath表示式,可是返回的結果總是null,糾結了很久,後來通過查資料,發現是我的KML中有名稱空間的緣故。

    首先,說明一些什麼是KML,因為下面的例子中會用到KML。KML是Keyhole Markup Language的縮寫,是一種基於XML 語法與格式的、用於描述和儲存地理資訊(如點、線、影象、多邊形和模型等)的編碼規範,可以被 Google Earth 和 Google Maps 識別並顯示。Google Earth 和 Google Maps 處理 KML 檔案的方式與網頁瀏覽器處理 HTML 和 XML 檔案的方式類似。Google Earth中通常使用KMZ檔案,KMZ檔案是壓縮過的KML檔案。目前,KML 是由開放地理空間聯盟(Open Geospatial Consortium, Inc.,簡稱 OGC)維護的國際標準。

    下面來看一個KML檔案例項,即本文中要用到的XML檔案:

    (KML檔案可以通過在Google Earth中得到,這裡我利用Google Earth搜尋長安大學渭水校區,然後新增地標,將位置另存為KML檔案)

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
	<name>長安大學渭水校區</name>
	<Style id="s_ylw-pushpin_hl">
		<IconStyle>
			<scale>1.3</scale>
			<Icon>
				<href>http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png</href>
			</Icon>
			<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
		</IconStyle>
		<ListStyle>
		</ListStyle>
	</Style>
	<Style id="s_ylw-pushpin">
		<IconStyle>
			<scale>1.1</scale>
			<Icon>
				<href>http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png</href>
			</Icon>
			<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
		</IconStyle>
		<ListStyle>
		</ListStyle>
	</Style>
	<StyleMap id="m_ylw-pushpin">
		<Pair>
			<key>normal</key>
			<styleUrl>#s_ylw-pushpin</styleUrl>
		</Pair>
		<Pair>
			<key>highlight</key>
			<styleUrl>#s_ylw-pushpin_hl</styleUrl>
		</Pair>
	</StyleMap>
	<Placemark>
		<name>長安大學渭水校區</name>
		<LookAt>
			<longitude>108.9032130001538</longitude>
			<latitude>34.36892100035922</latitude>
			<altitude>0</altitude>
			<heading>8.682278169459107e-011</heading>
			<tilt>0</tilt>
			<range>999.7995012938454</range>
			<gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
		</LookAt>
		<styleUrl>#m_ylw-pushpin</styleUrl>
		<Point>
			<gx:drawOrder>1</gx:drawOrder>
			<coordinates>108.9032130001538,34.36892100035922,0</coordinates>
		</Point>
	</Placemark>
</Document>
</kml>
    KML檔案可以使用Google Earth開啟,效果如下:

    現在進入正題,我們可以看到上面的XML檔案包含名稱空間,如果我們任然使用以前沒有名稱空間的方法用XPath獲取節點元素會出現什麼情況呢?

    例項如下:

public class XMLReader {
	public static void main(String[] args) throws DocumentException {
		SAXReader reader = new SAXReader();
		Document document = reader.read(new File("長安大學渭水校區.kml"));
		
		Node name = document.selectSingleNode("//name");
		if (name == null) {
			System.out.println("name節點為null!");
		} else {
			System.out.println(name.getText());
		}
	}
}

    執行結果如下:name節點為null!

    可是我的name節點明明不為null呀,這都是名稱空間惹的禍!

    下面我們著重看看解決辦法:

    方法一:設定你的xpath的名稱空間setNamespaceURIs

    例項如下:

public class KMLReader1 {

	public static void main(String[] args) throws DocumentException {
		SAXReader reader = new SAXReader();
		Document document = reader.read(new File("長安大學渭水校區.kml"));
		
		//方法一:設定你的xpath的名稱空間setNamespaceURIs
		Map<String, String> xmlMap = new HashMap<>();
		xmlMap.put("default", "http://www.opengis.net/kml/2.2");
		XPath xPath = document.createXPath("//default:name");
		xPath.setNamespaceURIs(xmlMap);
		Node name = xPath.selectSingleNode(document);
		System.out.println(name.getText());
		
	}
}

    首先,宣告一個Map物件,新增名稱空間,Map的鍵為名稱空間的名稱,這裡是預設名稱空間所以這裡Map的鍵可以隨便取,我取名叫default,Map的值為名稱空間的值,即http://www.opengis.net/kml/2.2。然後,宣告一個XPath物件,在createXPath方法中,要使用帶名稱空間字首的XPath表示式,即defau:name。最後,呼叫setNamespaceURIs方法,設定XPath的名稱空間。

    執行結果如下:

    長安大學渭水校區

    方法二:設定你的DocumentFactory()的名稱空間 setXPathNamespaceURIs

    例項如下:

public class KMLReader2 {

	public static void main(String[] args) throws DocumentException {
		//方法二:設定你的DocumentFactory()的名稱空間 setXPathNamespaceURIs
		Map<String, String> xmlMap = new HashMap<>();
		xmlMap.put("default", "http://www.opengis.net/kml/2.2");
		xmlMap.put("gx", "http://www.google.com/kml/ext/2.2");
		
		SAXReader reader = new SAXReader();
		reader.getDocumentFactory().setXPathNamespaceURIs(xmlMap);
		Document document = reader.read(new File("長安大學渭水校區.kml"));
		
		Node name = document.selectSingleNode("//default:name");
		System.out.println(name.getText());
		
		Node altitudeMode = document.selectSingleNode("//gx:altitudeMode");
		System.out.println(altitudeMode.getText());
	}
}

    這裡,我們設定的不是XPath的名稱空間了,而是DocumentFactory的名稱空間,其原理都差不多,只不過作用範圍不一樣,設定XPath的名稱空間作用在XPath表示式,設定DocumentFactory作用在整個Document物件上。所以這裡不再囉嗦,和上面一樣。

    執行結果如下:

    長安大學渭水校區
    relativeToSeaFloor

    方法三:不使用開發環境給你提供的一系列物件,而是用XPath語法中自帶的local-name() 和 namespace-uri()指定你要使用的節點名和名稱空間

    例項如下:

public class KMLReader3 {

	public static void main(String[] args) throws DocumentException {
		// 不使用開發環境給你提供的一系列物件,而是用XPath語法中自帶的local-name() 和 namespace-uri()
		// 指定你要使用的節點名和名稱空間
		SAXReader reader = new SAXReader();
		Document document = reader.read(new File("長安大學渭水校區.kml"));

		Node name = document
				.selectSingleNode("//*[local-name()='name' and namespace-uri()='http://www.opengis.net/kml/2.2']");
		System.out.println(name.getText());
	}
}

    這裡直接在XPath表示式中指出名稱空間,local-name()代表元素名稱,namespace-uri()代表元素所在名稱空間。

    執行結果如下:

    長安大學渭水校區

    方法四:不使用XPath表示式,直接用element的element方法取一個子元素或elementIterator方法取多個元素

    例項如下:

public class KMLReader4 {

	public static void main(String[] args) throws DocumentException {
		//不使用XPath,直接用element的element方法取一個子元素或elementIterator方法取多個元素
		SAXReader reader = new SAXReader();
		Document document = reader.read(new File("長安大學渭水校區.kml"));
		
		Element root = document.getRootElement();
		Element name = root.element("Document").element("name");
		System.out.println(name.getText());
	}
}

    這種方法是Dom4j的入門方法,這裡不再敘述。

    執行結果如下:

    長安大學渭水校區