[原创]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

|