您的位置:首页 > 编程语言 > Java开发

【嘿!Java】XPath在Java中的使用和小问题

2014-08-24 09:24 176 查看
博主最近看W3School的教程http://www.w3school.com.cn/xpath/index.asp,学了一些XPath的皮毛,这里面主要讲XPath的语法,具体如何在Java代码中使用XPath则参考这篇文章Java
语言的 XPath API。

博主参考第二篇文章,首先在这里给出示例Java代码,之后会说说使用过程中的小问题。下面的代码只做到查询出NodeList这一步,查询出的结果做什么样的处理(如:读取文本等)这里暂时不讨论。

后来,博主使用这段代码时产生了新需求。上面这段代码在new MyXPath(String fileName)时,必须提供一个包括其路径文件,比如在Java工程下有一个xml文件夹,里面存放了一个author.xml文件,就要写new MyXPath("xml/author.xml");。但有时候并没有一个已经存在的现成的文件,比如:通过发送网络请求,得到xml数据,因为不想将其以文件形式存储下来,所以只有存储它的流InputStream。

博主在网上搜了一些文章,了解到上面代码中generateDocument()方法里有句
doc = builder.parse(fileName);
这里fileName是形如"xml/author.xml"的字符串,其实这句parse()方法的参数也可以是InputStream类的

这下好办了,博主就将上面代码修改成下面这样。
import java.io.IOException;
import java.io.InputStream;
import java.lang.Double;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XPathInputStream
{
public XPath generateXPath()
{
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
return xpath;
}

public Document generateDocument(InputStream in)
{
DocumentBuilder builder = generateDocumentBuilder();
Document doc = null;
try
{
doc = builder.parse(in);
} catch (Exception e)
{
e.printStackTrace();
}
return doc;
}

public DocumentBuilder generateDocumentBuilder()
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);// never forget this!
DocumentBuilder builder = null;
try
{
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e)
{
e.printStackTrace();
}
return builder;
}

/**
* 查询节点集
* @param in 待查询的输入流
* @param exprString XPath查询语句
* return 查询到的节点集
*/
public NodeList queryNodelist(InputStream in,String exprString)
{
NodeList nodes = null;

XPath xpath=generateXPath();
Document doc=generateDocument(in);
try
{
XPathExpression expr = xpath.compile(exprString);//用XPath对象编译 XPath表达式
/*计算XPath表达式得到结果
*表达式是针对特定的上下文节点计算的,在这个例子中是整个文档
*还必须指定返回类型。这里要求返回一个节点集
*/
result = expr.evaluate(doc, XPathConstants.NODESET);
nodes = (NodeList) result;//将结果强制转化成NodeList
} catch (XPathExpressionException e)
{
e.printStackTrace();
}
return nodes;
}
}


问题描述

但是这段代码在运用过程中出现了一个问题:当用类似下面的代码连续解析同一个流in,会报错。

XPathInputStream.queryNodelist(in, "//disambiguation[1]/organization");
XPathInputStream.queryNodelist(in, "//interest/keyword");


原因分析

博主在网上查了,在使用xpath解析xml时,出现IOException里找到了答案。这位博主evaluate方法用的是Object evaluate(InputSource source, QName returnType),他是这么说自己代码报错原因的:

在每次调用Object evaluate(InputSource source, QName returnType)这个方法时都会重新生成DocumentBuilder对象来parse InputSource,每次parse都会把整个文件都解析成dom树并load到内存中,然后关闭流,在第二次parse同一个Inputsource的时候便产生了IOException

博主用的是Object evaluate(Document doc, QName returnType),与上面不一样,但根据这个说法类推——博主的代码里每次调用XPathUtils.queryNodelist()都会执行一次generateDocument(in),里面有一句doc = builder.parse(in);,可以猜测这句话执行完会关闭流,所以对同一个流in再次调用XPathInputStream.queryNodelist();时,in已经被关闭,造成报错。

博主没有看XPath的源代码,这只是自己猜想,如果哪位大神知道真正的原因,告诉博主呀~~


解决办法

博主猜测上面的原因后,想如果连续同一个流in,只调用一次doc = builder.parse(in);,是不是就不会报错了呢?为此,博主修改了代码,实验了之后发现真的没有再报错,说明博主猜想应该没有错。

连续调用代码如下,不会报错

MyXPathInputStream.queryNodelist("//disambiguation[1]/organization");
MyXPathInputStream.queryNodelist("//interest/keyword");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: