[原创]JDOM and XML 解析, Part 2

类别:Java 点击:0 评论:0 推荐:

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