[原创]JDOM and XML 解析, Part 2
用命名空间进行工作
JDOM为XML命名空间提供了丰富的,本地支持。在命名空间发布之后JDOM才被发布。在JDOM中,命名空间是通过Namespace类来描述的:
Namespace xhtml = Namespace.getNamespace(
"xhtml", "http://www.w3.org/1999/xhtml";);
通过构造,一个对象被赋予了一个名字并且能随意的给一个命名空间:
elt.addContent(new Element("table", xhtml));
如果没有给出的命名空间,被构造的元素将没有命名空间。一个元素的命名空间是它类型的本质的一部分,所以JDOM确保元素移动到文档的其它位置命名空间将不能被更改。如果一个元素没有命名空间并且移动到一个有命名空间的元素之下,它不继承命名空间。有时这将造成混淆,直到你学习了将textual描述从语义结构中分离。
XMLOutputter类挑选出命名空间,并且保证所有的”xmlns”声明在适当的位置。默认的,类的声明位置是必需的。如果你希望他们被进一步声明为树的形式,有可以用element.addNamespaceDecalaration()方法去提供一个指引。
所有JDOM元素和属性存取方法支持一个可选择的命名空间参数,这个参数指明了查看的是哪个命名空间。下面的例子指向了命名空间”xhtml”:
List kids = html.getChildren("title", xhtml);
Element kid = html.getChild("title", xhtml);
Attribute attr = kid.getAttribute("id", xhtml);
当调用存取方法的时候,仅仅和统一资源定位符(URLs)有关。这是因为XML Namespaces的工作方式。
如果没有为存取方法提供命名空间实例,那么将在没有命名空间的前提下搜索元素。JDOM用非常表面的描述方式去进行描述。没有命名空间意味着没有命名空间,没有上级命名空间或者可能产生新的bug。
关于ResultSetBuilder
ResultSetBuilder是对 JDOM的一个扩展,为了那些需要用XML文档去处理SQL结果集的那些人准备的。可以在org.jdom.conrib.input包里找到它。
ResultSetBuilder构造器接受一个java.sql.ResultSet作为输入参数,并且从它的build()方法返回一个org.jdom.Document。
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select id, name from registry");
ResultSetBuilder builder = new ResultSetBuilder(rs);
Document doc = builder.build();
如果你不提供指定的配置信息,上面的代码构造的文档类似如下:
<result>
<entry>
<id>1</id>
<name>Alice</name>
</entry>
<entry>
<id>2</id>
<name>Bob</name>
</entry>
</result>
ResultSetBuilder类用查询的ResultSetMetaData类去决定列名,并且他们作为元素的名字。默认的,根元素有一个叫”result”的名字,并且每行有一个叫”entry”的名字。这些名字能够通过setRootName(String name)方法和setRowName(String name)方法进行更改。你也可以用setNamespace(Namespaces)方法为文档标识命名空间。
如果你希望要ResultSet列描述作为一个XML属性而不是一个元素,你可以调用setAsAttribute(String columnName)方法或者setAsAttribute(String columnName, String attributeName)方法—后面的方法改变属性的名字。你也可以用setAsElement(String columnName, String elemName)方法重命名。所有的这些方法都接受名字的索引。下面的代码片段用这些方法产生一个文档:
ResultSetBuilder builder = new ResultSetBuilder(rs);
builder.setAsAttribute("id");
builder.setAsElement("name", "fname");
Document doc = builder.build();
<result>
<entry id="1">
<fname>Alice</fname>
</entry>
<entry id="2">
<fname>Bob</fname>
</entry>
</result>
ResultSetBuilder类没有提供任何机制在数据库中存储XML文档。为了实现这个任务,你可以用一个本地XML书库,例如Oracle9i设置XML DB的性能。
内置XSLT
现在我们忽略核心库的基础东西,看一些高性能的处理,例如XSLT。
XSLT提供了将XML文档从一种格式转换为另一种格式的标准方法。用一个XML文件去处理变化,通常用已经存在的XML作为一个XHTML Web页面,或者将XML文档从一种模式转化为另一种模式。JDOM内置支持在内存中XSLT转化方式,用JAXP标准接口实现XSLT引擎。关键类是JDOMSource和JDOMResult,两种类都在org.jdo.transform包中。JDOMSource提供了一个JDOM文档作为转换的输入,JDOMResult捕获一个结果作为JDOM文档。Listing 1演示了一个基于内存转换的完整程序:
Code Listing 1: XSL Transform
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;
import org.jdom.transform.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class XSLTransform {
public static void main(String[] args) throws Exception {
// Build the base document, just assume we get 2 params
String docname = args[0];
String sheetname = args[1];
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(docname);
// Use JAXP to get a transformer
Transformer transformer = TransformerFactory.newInstance()
.newTransformer(new StreamSource(sheetname));
// Run the transformation
JDOMSource source = new JDOMSource(doc);
JDOMResult result = new JDOMResult();
transformer.transform(source, result);
Document doc2 = result.getDocument();
// Display the results
XMLOutputter outp = new XMLOutputter();
outp.setTextNormalize(true);
outp.setIndent(" ");
outp.setNewlines(true);
outp.output(doc2, System.out);
}
}
你能够混合和匹配多种源和结果的实现。例如,如果你知道你仅仅打算输出一个文档并且不需要基于内存转换的JDOM描述,你可以用一个import javax.xml.transform.stram.StreamResult来替代它:
JDOMSource source = new JDOMSource(doc);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);
XPath Included
XPath提供一个利用字符串搜索路径去查阅XML文档的机制。用XPath,你可以避免遍历文档,通过简单路径表达式仅仅解析你想要的信息。例如,让我们看下面的XHTML文档:
<table border="1">
<tr>
<th> </th>
<th>Open</th>
<th>Close</th>
</tr>
<tr>
<td>Sunday</td>
<td>11am</td>
<td>4pm</td>
</tr>
<tr>
<td>Monday</td>
<td>9am</td>
<td>5pm</td>
</tr>
</table>
在Beta8版本以后,JDOM提供了对XPath的内置支持,用org.jdom.xpath.XPath来完成。为了用XPath,你首先得利用XPath.newInstance()来构造一个XPath实例。
XPath xpath = XPath.newInstance("/some/xpath");
然后调用selectNodes()去获得基于给定上下文答案的一个列表。例如,这个上下文可以是一个文档或者是文档中的一个元素。
List results = xpath.selectNodes(doc);
有其他的方法去获得单个节点,数字值,字符串值等等。默认的XPath实现用Jaxen,可以参考http://jaxen.org。
Listing2代码用XPath从web.xml文件中找出servlet部署描述的信息。给一个web.xml文件,Listing 3显示了一个web.xml文件:
Code Listing 2: XPath Pulls Information
import java.io.*;
import java.util.*;
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;
import org.jdom.xpath.*;
public class XPathReader {
public static void main(String[] args) throws IOException, JDOMException {
if (args.length != 1) {
System.err.println("Usage: samples.XPathReader [web.xml]");
return;
}
String filename = args[0];
PrintStream out = System.out;
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(new File(filename));
// Print servlet information
XPath servletPath = XPath.newInstance("//servlet");
List servlets = servletPath.selectNodes(doc);
out.println("This WAR has "+ servlets.size() +" registered servlets:");
Iterator i = servlets.iterator();
while (i.hasNext()) {
Element servlet = (Element) i.next();
out.print("\t" + servlet.getChild("servlet-name")
.getTextTrim() +
" for " + servlet.getChild("servlet-class")
.getTextTrim());
List initParams = servlet.getChildren("init-param");
out.println(" (it has " + initParams.size() + " init params)");
}
// Print security role information
// Notice how we're directly fetching Text content
XPath rolePath = XPath.newInstance("//security-role/role-name/text()");
List roleNames = rolePath.selectNodes(doc);
if (roleNames.size() == 0) {
out.println("This WAR contains no roles");
}
else {
out.println("This WAR contains " + roleNames.size() + " roles:");
i = roleNames.iterator();
while (i.hasNext()) {
out.println("\t" + ((Text)i.next()).getTextTrim());
}
}
}
}
Code Listing 3: Example web.xml File (for use with XPath process in Listing 2)
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
<servlet>
<servlet-name>
snoop
</servlet-name>
<servlet-class>
SnoopServlet
</servlet-class>
</servlet>
<servlet>
<servlet-name>
file
</servlet-name>
<servlet-class>
ViewFile
</servlet-class>
<init-param>
<param-name>
initial
</param-name>
<param-value>
1000
</param-value>
<description>
The initial value for the counter <!-- optional -->
</description>
</init-param>
</servlet>
<distributed/>
<security-role>
<role-name>
manager
</role-name>
<role-name>
director
</role-name>
<role-name>
president
</role-name>
</security-role>
</web-app>
程序输出如下:
This WAR has 2 registered servlets:
snoop for SnoopServlet (it has 0 init params)
file for ViewFile (it has 1 init params)
This WAR contains 3 roles:
manager
director
president
本文地址:http://com.8s8s.com/it/it15777.htm