xPath : tutorial

Navigate with XPath

Ref : link1

XPath หมายถึงภาษานึงที่ใช้งานร่วมกับ XML เพื่อใช้ในการดึงข้อมูลออกจากเอกสาร XML เป็นภาษาง่าย ๆ ในการใช้งาน สมมติเรามีข้อมูล XML ดังนี้ (ไฟล์ Berryz_Kobo.xml ในตัวอย่าง)

ปล , สามารถโหลด Code ตัวอย่าง ไฟล์ข้อมูลทั้งหมดได้ที่นี่ -> Example - XPath with Java.rar

--------------------------------------------------------------------------------------

Shimizu Saki
Tsugunaga Momoko
Sugaya Risako
Nutsuyaki Miyabi
Kumai Yurina
Tokunaga Chinami
Sudou Maasa
Ishimura Maiha


Anata Nashide wa Sekite Yukenai


Fighting Pose wa Date Janai


Piriri To Ikou


Happiness Koufuku Kangei


Koi no Jubaku


Special Generation


Nanchuu Koi wo Yatteruu YOU KNOW


21ji made no Cinderella


Gyagu 100kai bun Aishite Kudasai


Jiriri Kiteru


Waracchaou yo BOYFRIEND


Munasawagi Scarlet


VERY BEAUTY


Kokuhaku no Funsui Hiroba


Tsukiatte Iru noni Kataomoi



--------------------------------------------------------------------------------------
จากข้อมูล XML ที่ให้มา สมมติว่าต้องการจะอ่านข้อมูลชื่อ Single ทั้ง 15 ออกมา หากใช้ Java ปกติอ่านเข้ามา จะใช้ DOM (Document Object Model) อ่านเข้ามา ดัง Code ข้างล่าง (สร้างไฟล์อ่าน XML ชื่อ XMLRead.java)
--------------------------------------------------------------------------------------
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

public class XMLRead {

public static void main(String[] args) throws Exception {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new File("Berryz_Kobo.xml"));
Node berryzNode = doc.getFirstChild();
System.out.println("ROOT NODE NAME = "+berryzNode.getNodeName());
NodeList childNodes = berryzNode.getChildNodes();
for(int i=0; i < childNodes.getLength() ; i++){
Node dataNode = childNodes.item(i);
if( "singles".equalsIgnoreCase(dataNode.getNodeName())){
NodeList singleNodes = dataNode.getChildNodes();
for(int j=0; j < singleNodes.getLength() ; j++){
Node singleNode = singleNodes.item(j);
if( "single".equalsIgnoreCase( singleNode.getNodeName()) ){
NodeList nameNodes = singleNode.getChildNodes();
for(int k=0; k < nameNodes.getLength() ; k++){
Node nameNode = nameNodes.item(k);
if( "name".equalsIgnoreCase( nameNode.getNodeName())){
NodeList textNodes = nameNode.getChildNodes();
for(int l=0; l < textNodes.getLength() ; l++){
Node textNode = textNodes.item(l);
if( textNode.TEXT_NODE == textNode.getNodeType() ){
System.out.println("SINGLE = "+textNode.getNodeValue());
}
}
}
}
}
}
}
}
}

}
--------------------------------------------------------------------------------------
เมื่อสั่งรันดูจะได้ผลดังนี้ (ต้องมีไฟล์ Berryz_Kobo.xml อยู่ในโฟลเดอร์เดียวกับไฟล์ XMLRead.java ด้วย)

java_xml_xpath_1


รูปแบบการอ่านของ DOM จะเป็นแบบ Tree ที่เริ่มจาก Root Node เสมอ ดังนั้นเมื่อสั่ง doc.getFirstChild() จะได้ berryzNode มา และจาก Root Node เราสามารถเข้าถึง Node ย่อย ๆ ได้ โดยเรียก getChildNodes() method ซึ่งการที่เราจะเอาชื่อ Single ออกมา ก็ต้องเข้าไปถึง “name” node ที่อยู่ภายใต้ “single” node (แต่ละ tag ก็คือ node ย่อยนั่นเอง) มีข้อสังเกตนิดนึงว่าข้อความ Text ใน Node นั้น จะถูกถือเป็น childNode ของ node นั้น ๆ โดยมี Type เป็น TEXT_NODE (จะเห็นว่าไม่ใช่ getNodeValue() ของ Node “name” แต่เป็น getNodeValue() ของ childNode ของ “name” Node ที่มี Type เป็น TEXT_NODE แทน)
จะเห็นว่ากว่าจะได้ข้อความมาแต่ละอันนั้นช่างลำบากยากเย็นเสียเหลือเกิน วนลูปกันจนเหนื่อย XPath สามารถช่วยลดเวลาเราได้ ดังนี้
สร้างไฟล์ XpathRead.java ขึ้นมา มี Code ดังข้างล่าง (ไว้ที่เดียวกับ XMLRead.java และ Berryz_Kobo.xml)
--------------------------------------------------------------------------------------
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

public class XPathRead {

public static void main(String[] args) throws Exception {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new File("Berryz_Kobo.xml"));

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//name/text()");

Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList textNameNodes = (NodeList) result;
for (int i = 0; i < textNameNodes.getLength(); i++) {
Node textNode = textNameNodes.item(i);
System.out.println("Single = "+textNode.getNodeValue());

}
}

}
--------------------------------------------------------------------------------------
เมื่อสั่งรันโปรแกรม ได้ผลดังนี้

java_xml_xpath_2

สังเกตว่าใช้ XPath Query เป็น “//name/text()” ในการดึง text node ออกมาจาก node “name” โดย // หมายถึงให้วิ่งหา node ทุกตัวภายใต้ XML ปัจจุบัน (เมื่อใส่ //name จึงหมายถึงหา Node ที่มีชื่อว่า “name” ออกมาจาก XML ) /text() คือให้เอา text node ภายใต้ node ก่อนหน้าออกมา
ในความจริงการใช้ // ค่อนข้างจะเปลือง Performance ของโปรแกรมพอสมควร (เพราะวิ่งหาทั้ง XML เลย) เราสามารถเขียนให้ยาวขึ้นหน่อยนึง แต่มีประสิทธิภาพดีขึ้น โดยใช้ XPath Query เป็น “/berryz/singles/single/name/text()” (ทดลองเปลี่ยนดูได้ จะพบว่าให้ผลเหมือนเดิมเป๊ะ)

อันนี้เป็นเรื่องปกติของ XML คนที่ศึกษาอาจจะรู้อยู่แล้ว ทีนี้มาถึงเรื่องหลักที่อยากเขียนจริง ๆ เกี่ยวกับการใช้ XPath กับ Java ก็คือ เมื่อคุณไปเจอ XML ที่มีการระบุ Namespace คุณจะพบว่าการอ้างชื่อของ XML นั้นด้วย XPath จะไม่สามารถทำได้ ดังนี้
1. สร้างไฟล์ XML ที่มีการระบุ Namespace ชื่อ Berryz_Kobo_NS.xml ข้างในมีเนื้อหาดังนี้
--------------------------------------------------------------------------------------
xmlns="http://boris-chung.live.space.com">
Shimizu Saki
Tsugunaga Momoko
Sugaya Risako
Nutsuyaki Miyabi
Kumai Yurina
Tokunaga Chinami
Sudou Maasa
Ishimura Maiha


Anata Nashide wa Sekite Yukenai


Fighting Pose wa Date Janai


Piriri To Ikou


Happiness Koufuku Kangei


Koi no Jubaku


Special Generation


Nanchuu Koi wo Yatteruu YOU KNOW


21ji made no Cinderella


Gyagu 100kai bun Aishite Kudasai


Jiriri Kiteru


Waracchaou yo BOYFRIEND


Munasawagi Scarlet


VERY BEAUTY


Kokuhaku no Funsui Hiroba


Tsukiatte Iru noni Kataomoi



--------------------------------------------------------------------------------------
สังเกตว่ามีการใส่ xmlns=http://boris-chung.live.space.com เพิ่มเติมเข้าไปใน root node แบบนี้คือการเพิ่ม Namespace เข้าไป การเพิ่มเข้าไปแบบนี้จะทำให้ childNode ของ node ที่เพิ่ม มีผลอยู่ใน Namespace ที่กำหนดด้วย
2. ลองสร้างไฟล์ XPathReadNS.java ขึ้นมา ข้างในมีเนื้อหาเหมือนไฟล์ XPathRead.java เพียงแต่เปลี่ยนไปอ่านไฟล์ Berryz_Kobo_NS.xml แทน ดังนี้
--------------------------------------------------------------------------------------

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

public class XPathReadNS {

public static void main(String[] args) throws Exception {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new File("Berryz_Kobo_NS.xml"));

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("/berryz/singles/single/name/text()");

Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList textNameNodes = (NodeList) result;
for (int i = 0; i < textNameNodes.getLength(); i++) {
Node textNode = textNameNodes.item(i);
System.out.println("Single = "+textNode.getNodeValue());

}
}

}
--------------------------------------------------------------------------------------
เมื่อสั่งรันโปรแกรมดู จะได้ผลดังนี้

java_xml_xpath_3

ไม่ปรากฏอะไรขึ้นมาเลย (อันนี้รันบน Editplus พอรันเสร็จมันจะหยุดให้กดคีย์ใด ๆ ให้อัตโนมัติ แต่ถ้ารันบน Command-Prompt จะผ่านไปเลย) ทั้งนี้เพราะ XPath ไม่สามารถเข้าถึง Node ใด ๆ ที่มีการกำหนด Namespace เอาไว้ได้ หากไม่กำหนด Namespace ให้กับ XPath ก่อน ซึ่งขั้นตอนในการที่จะทำให้ XPath สามารถใช้งานกับ Node ที่มีการกำหนด Namespace เอาไว้นั้น ทำได้ดังนี้
1) สร้าง Class ที่ implement class “NamespaceContext” ดังนี้ (ในที่นี้ใช้ชื่อไฟล์ NamespaceContextProvider.java)
--------------------------------------------------------------------------------------

import java.util.Iterator;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;

public class NamespaceContextProvider implements NamespaceContext {
String boundPrefix, boundURI;

NamespaceContextProvider (String prefix, String URI) {
boundPrefix = prefix;
boundURI = URI;
}

public String getNamespaceURI (String prefix) {
if (prefix.equals(boundPrefix)) {
return boundURI;
}
else if (prefix.equals(XMLConstants.XML_NS_PREFIX)) {
return XMLConstants.XML_NS_URI;
}
else if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) {
return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
}
else {
return XMLConstants.NULL_NS_URI;
}
}

public String getPrefix (String namespaceURI) {
if (namespaceURI.equals(boundURI)) {
return boundPrefix;
}
else if (namespaceURI.equals(XMLConstants.XML_NS_URI)) {
return XMLConstants.XML_NS_PREFIX;
}
else if (namespaceURI.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
return XMLConstants.XMLNS_ATTRIBUTE;
}
else {
return null;
}
}

public Iterator getPrefixes (String namespaceURI) {
return null;
}
}
--------------------------------------------------------------------------------------
ในการ implement class นี้ต้อง implement 3 method โดยเราจะกำหนด prefix ที่ต้องการใช้จับคู่กับ Namespace และค่า uri ของ Namespace ของ tag นั้น ๆ ตอนนี้ดู ๆ ไปอาจจะยังไม่เข้าใจ ให้เข้าใจง่าย ๆ ว่า Copy ไปเลยก็แล้วกัน ง่ายดี
2) ใน Code ที่จะเรียกใช้งาน XPath นั้น เพิ่มการกำหนด Namespace เข้าไปดังนี้
--------------------------------------------------------------------------------------

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

public class XPathReadNS {

public static void main(String[] args) throws Exception {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new File("Berryz_Kobo_NS.xml"));

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

//Namespace Specify
NamespaceContext nsContext = new NamespaceContextProvider("sinName" , "http://boris-chung.live.space.com");
xpath.setNamespaceContext(nsContext);

XPathExpression expr = xpath.compile("/sinName:berryz/sinName:singles/sinName:single/sinName:name/text()");

Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList textNameNodes = (NodeList) result;
for (int i = 0; i < textNameNodes.getLength(); i++) {
Node textNode = textNameNodes.item(i);
System.out.println("Single = "+textNode.getNodeValue());

}
}

}
--------------------------------------------------------------------------------------
ชื่อ prefix นั้นเราจะกำหนดเป็นอะไรก็ได้ (ตามใจเรา) แต่จะต้องใช้ให้ตรงกันกับใน XPath Query ด้วย จากตัวอย่างคือ sinName โดยใช้กับ Namespace “http://boris-chung.live.space.com” และสังเกตว่าใน XPath Query นั้นตอนที่ระบุชื่อ tag จะต้องใส่ prefix นำหน้าทุกตัว (อย่าลืมว่า childNode ของ Node ที่กำหนด Namespace นั้น ก็จะถือว่ามี Namespace นั้นไปด้วย)
เมื่อรันไฟล์ออกมาได้ผลลัพธ์เหมือนเดิมดังนี้

java_xml_xpath_4


สุดท้ายจะแถมเกี่ยวกับการใช้ XPath Query แบบที่กำหนดเงื่อนไขของ Attribute ให้ดูเป็นตัวอย่างเผื่อจะเอาไปใช้กัน สมมติว่าจะ Query สมาชิกที่ graduated ไปแล้ว (จากข้อมูล XML คือ “Ishimura Maiha”) ออกมา จะใช้ XPath Query ดังนี้
“/berryz/member[@graduated=’true’]/text()”
ให้ลองเอา XPath Query นี้ไปแทนในไฟล์ XPathRead.java ดู จะได้ผลลัพธ์ดังนี้

java_xml_xpath_5

การใช้ @ เป็นการอ้างอิงถึง Attribute ของตัว Node ซึ่งในที่นี้เราอ้างถึงเฉพาะ member ที่มีค่า graduated เป็น “true” เท่านั้น จึงได้ Ishimura Maiha ออกมา

ก็เอามาฝากครับสำหรับท่านทียังไม่ทราบ ส่วนท่านใดที่ทราบแล้วขาดตกบกพร่องอย่างไรเพิ่มเติมได้เลยนะครับ

วิธีการทำ URL ภาษาไทยตอนสมัคร Blogger นะครับ

ก่อนอื่นเลยหา คำที่เราต้องการจะใช้ เช่น ในที่นี้ผมใช้ "โลกร้อน" นะครับ

หลังจากได้คำเสร็จแล้วก็เข้าไปสู่ หมาไฟ(FireFox) และพิมพ์ลงไปในช่อง URL เลยครับว่า

http://โลกร้อน.blogspot.com

หลังจากกด Enter URL ของเราจะถูกแปลงเป็นภาษาต่างด๊าว เอ้ย ภาษา English จะเป็นดังนี้ครับ

http://xn--12c2c3ag6byh3b.blogspot.com/

ให้เรา Copy "xn--12c2c3ag6byh3b" มาครับ

เข้าไปที่หน้าสร้าง Blog ใหม่ตรงช่องที่ให้ใส่ URL.blogspot.com

ก็ใส่ xn--12c2c3ag6byh3b เลยครับแล้วกด Check Available

เท่านี้ก็เป็นอ้นเสร็จเรียบร้อยครับผม

Ref : link1
top