19

我有一个 1000 条目文档,其格式类似于:

<Example>
     <Entry>
          <n1></n1>
          <n2></n2>
      </Entry>
      <Entry>
          <n1></n1>
          <n2></n2>
      </Entry>
      <!--and so on-->

这里有1000多个Entry节点。我正在编写一个 Java 程序,它基本上一个一个地获取所有节点,并对每个节点进行一些分析。但问题是节点的检索时间随着节点数的增加而增加。例如,检索第一个节点需要 78 毫秒,检索第二个节点需要 100 毫秒,并且它不断增加。检索 999 节点需要 5 秒以上。这是非常缓慢的。我们会将此代码插入到具有超过 1000 个条目的 XML 文件中。有些人喜欢数百万。解析整个文档的总时间超过 5 分钟。

我正在使用这个简单的代码来遍历它。这nxp是我自己的类,它具有从 xpath 获取节点的所有方法。

nxp.fromXpathToNode("/Example/Entry" + "[" + i  + "]", doc);    

并且doc是文件的文档。i是要检索的节点编号。

另外,当我尝试这样的事情时

List<Node> nl = nxp.fromXpathToNodes("/Example/Entry",doc);  
      content = nl.get(i);    

我面临同样的问题。

任何人都有关于如何加速节点的任何解决方案,因此从 XML 文件中获取第一个节点和第 1000 个节点需要相同的时间。


这是 xpathtonode 的代码。

public Node fromXpathToNode(String expression, Node context)  
{  
    try  
    {  
        return (Node)this.getCachedExpression(expression).evaluate(context, XPathConstants.NODE);  
    }  
    catch (Exception cause)  
    {  
        throw new RuntimeException(cause);  
    }  
}  

这是 fromxpathtonodes 的代码。

public List<Node> fromXpathToNodes(String expression, Node context)  
{  
    List<Node> nodes = new ArrayList<Node>();  
    NodeList results = null;  
    
    try  
    {  
        results = (NodeList)this.getCachedExpression(expression).evaluate(context, XPathConstants.NODESET);  
          
        for (int index = 0; index < results.getLength(); index++)  
        {  
            nodes.add(results.item(index));  
        }  
    }  
    catch (Exception cause)  
    {  
        throw new RuntimeException(cause);  
    }  
    
    return nodes;  
}  

这是开始

public class NativeXpathEngine implements XpathEngine  
{      
private final XPathFactory factory;  
  
private final XPath engine;  

/**
 * Cache for previously compiled XPath expressions. {@link XPathExpression#hashCode()}
 * is not reliable or consistent so use the textual representation instead.
 */  
private final Map<String, XPathExpression> cachedExpressions;  
  
public NativeXpathEngine()  
{
    super();  
    
    this.factory = XPathFactory.newInstance();  
    this.engine = factory.newXPath();  
    this.cachedExpressions = new HashMap<String, XPathExpression>();  
}  
4

6 回答 6

10

Try VTD-XML. It uses less memory than DOM. It is easier to use than SAX and supports XPath. Here is some sample code to help you get started. It applies an XPath to get the Entry elements and then prints out the n1 and n2 child elements.

final VTDGen vg = new VTDGen();
vg.parseFile("/path/to/file.xml", false);

final VTDNav vn = vg.getNav();
final AutoPilot ap = new AutoPilot(vn);
ap.selectXPath("/Example/Entry");
int count = 1;
while (ap.evalXPath() != -1) {
    System.out.println("Inside Entry: " + count);

    //move to n1 child
    vn.toElement(VTDNav.FIRST_CHILD, "n1");
    System.out.println("\tn1: " + vn.toNormalizedString(vn.getText()));

    //move to n2 child
    vn.toElement(VTDNav.NEXT_SIBLING, "n2");
    System.out.println("\tn2: " + vn.toNormalizedString(vn.getText()));

    //move back to parent
    vn.toElement(VTDNav.PARENT);
    count++;
}
于 2010-08-10T12:18:02.757 回答
7

正确的解决方案是在调用 item(i) 后立即分离节点,如下所示:

Node node = results.item(index)
node.getParentNode().removeChild(node)
nodes.add(node)

请参阅XPath.evaluate 性能在多次调用中减慢(荒谬地)

于 2013-02-27T00:55:13.910 回答
4

我在 Xpath 评估中遇到了类似的问题,我尝试使用 CachedXPathAPI,它比之前使用的 XPathApi 快 100 倍。此处提供了有关此 Api 的更多信息:http: //xml.apache.org/xalan-j/apidocs/org/apache/xpath/CachedXPathAPI.html

希望能帮助到你。干杯,马杜苏丹

于 2012-03-12T12:05:21.363 回答
2

如果您需要解析庞大而扁平的文档,SAX 是一个不错的选择。它允许您将 XML 作为流来处理,而不是构建一个巨大的 DOM。您的示例可以使用这样的 ContentHandler 进行解析:

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;

public class ExampleHandler extends DefaultHandler2 {

    private StringBuffer chars = new StringBuffer(1000);

    private MyEntry currentEntry;
    private MyEntryHandler myEntryHandler;

    ExampleHandler(MyEntryHandler myEntryHandler) {
        this.myEntryHandler = myEntryHandler;
    }

    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        chars.append(ch);
    }

    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if ("Entry".equals(localName)) {
            myEntryHandler.handle(currentEntry);
            currentEntry = null;
        }
        else if ("n1".equals(localName)) {
            currentEntry.setN1(chars.toString());
        }
        else if ("n2".equals(localName)) {
            currentEntry.setN2(chars.toString());
        }
    }


    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes atts) throws SAXException {
        chars.setLength(0);
        if ("Entry".equals(localName)) {
            currentEntry = new MyEntry();
        }
    }
}

如果文档的结构更深更复杂,您将需要使用 Stacks 来跟踪文档中的当前路径。然后,您应该考虑编写一个通用的 ContentHandler 来完成繁琐的工作并与您的文档类型相关的处理程序一起使用。

于 2010-03-02T19:54:33.027 回答
1

你使用什么样的解析器?

DOM 将整个文档拉入内存 - 一旦将整个文档拉入内存,您的操作就会很快,但在 Web 应用程序或 for 循环中这样做会产生影响。

SAX 解析器进行按需解析并在您请求时加载节点。

因此,请尝试使用适合您需要的解析器实现。

于 2010-03-02T17:33:52.003 回答
0

JAXEN库用于 xpath:http: //jaxen.codehaus.org/

于 2010-03-02T17:43:26.537 回答