XML文档转换StackOverflowError

时间:2015-08-27 16:24:25

标签: java xml dom stack-overflow

我正在从头开始构建XML文档。我写了一个类来插入和遍历XML中的元素。就是这样:

import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class XMLParser {

    private Document doc;
    private Node currentNode;

    public XMLParser(String path) {

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();

            doc = builder.newDocument();
            currentNode = doc.createElement("Root");
            ((Element) currentNode).setAttribute("Path", path);
            doc.appendChild(currentNode);


        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public XMLParser add(String name){
        currentNode = currentNode.appendChild(doc.createElement(name));

        return this;
    }

    public XMLParser attr(String name, String value){
        ((Element) currentNode).setAttribute(name, value);

        return this;
    }

    public XMLParser set(String value){
        currentNode = currentNode.appendChild(doc.createTextNode(value));

        return this;
    }

    public XMLParser up(){
        currentNode = currentNode.getParentNode();

        return this;
    }

    public String toXML(){
        final Transformer transformer;
        try {
            transformer = TransformerFactory.newInstance().newTransformer();
        } catch (final TransformerConfigurationException ex) {
            throw new IllegalStateException(ex);
        }
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
        final StringWriter writer = new StringWriter();
        try {
            transformer.transform(
                new DOMSource(doc),
                new StreamResult(writer)
            );
        } catch (final TransformerException ex) {
            throw new IllegalArgumentException(ex);
        }
        return writer.toString();
    }


}

现在我可以调用“new XMLParser(”“)。add(”A“)。up()。add(”B“)。toXML();”生成包含XML代码的String。

这适用于较小的文档,但如果xml变大,我会在toXML()的transformer.transform(...)中得到Stackoverflow-Error:

Exception in thread "main" java.lang.StackOverflowError
at com.sun.org.apache.xml.internal.serializer.ToStream.characters(Unknown Source)
at com.sun.org.apache.xml.internal.serializer.ToUnknownStream.characters(Unknown Source)
at com.sun.org.apache.xml.internal.serializer.ToUnknownStream.characters(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
at com.sun.org.apache.xalan.internal.xsltc.trax.DOM2TO.parse(Unknown Source)
...[1000 lines in console buffer displayed]

有没有人知道如何最小化堆栈开销?我不想摆弄JVM设置,因为我希望它也可以在不同的计算机上运行......

提前感谢您的想法或解决方案!

修改 下面是使用目录的Java文件对象调用的递归代码,该目录遍历内部文件夹以从目录内容构建xml-tree

private void stepAhead(File f) throws IOException {
    if(f.isDirectory()){
        File[] ergebnis = f.listFiles();
        ArrayList<File> dateien = new ArrayList<File>();
        for (File temp : ergebnis) {
            if (temp.isDirectory()){
                //System.out.println("Checking Directory: " + temp.getCanonicalFile());
                xml.add("Dir").attr("Name", temp.getName());
                stepAhead(temp);
                xml.up();
            }
            else if(temp.isFile()){
                dateien.add(temp);
            }
            else{
                throw new RuntimeException("Keine Datei und kein Ordner");
            }

        }
        for(File temp : dateien){
            BasicFileAttributes attributes = Files.readAttributes(temp.toPath(), BasicFileAttributes.class);

            xml.add("File");
            xml.add("Name").set(temp.getName()).up();
            xml.add("Size").set(""+attributes.size()).up();
            xml.add("DateCreated").set(formatFileTime(attributes.creationTime())).up();
            xml.add("DateLastModified").set(formatFileTime(attributes.lastModifiedTime())).up();
            xml.up();
        }
    }
}

xml XMLParser 对象,我只是在构造函数中初始化它

2 个答案:

答案 0 :(得分:2)

XMLParser.set(String value)存在问题以及如何构建目录文档:

XMLParser.set(String value)附加一个文本节点并将当前节点设置为该文本节点。

根据stepAhead中的构建器代码,您将在元素的一个级别和文本节点的另一个级别上下载,但是您只上升一个级别。

xml.add("Name").set(temp.getName()).up();

当应用于充满文件的深层目录时,输出是一个令人难以置信的深层嵌套文档。 Transformer实现使用递归来遍历文档,然后发生堆栈溢出。

如果您将XMLParser.set(String value)更改为

public XMLParser set(String value){
    currentNode.appendChild(doc.createTextNode(value));
    return this;
}

一切正常,输出看起来不错。 (在打印简单目录的结果时,您应该已经看到了拧紧的输出!)

无论如何,XMLParser是一个智能类,消除了DOM操作的痛苦。也许最好将它命名为XMLBuilder。

答案 1 :(得分:1)

虽然可能有点复杂,但请使用XMLStreamWriter。这将允许流无限大的 XML文件。

你必须生成自己的缩进逻辑,但是,实现这样的细节就是为什么你有一个XMLParser类,对吧?顺便说一句,这应该真的命名为XMLGeneratorXMLBuilder,你不觉得吗?

要展示如何使用XMLStreamWriter,这里有一些代码(大部分)使用您的示例(new XMLParser("").add("A").up().add("B").toXML()):

XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(System.out);
try {
    xmlStreamWriter.writeStartDocument();
    xmlStreamWriter.writeCharacters("\n");

    // <Root> ...
    xmlStreamWriter.writeStartElement("Root");
    xmlStreamWriter.writeAttribute("Path", "...");
    xmlStreamWriter.writeCharacters("\n");

        // <A x="abc">Hello World</A>
        xmlStreamWriter.writeCharacters("   ");
        xmlStreamWriter.writeStartElement("A");
        xmlStreamWriter.writeAttribute("x", "abc");
        xmlStreamWriter.writeCharacters("Hello World");
        xmlStreamWriter.writeEndElement(); // end of A
        xmlStreamWriter.writeCharacters("\n");

        // <B></B>
        xmlStreamWriter.writeCharacters("   ");
        xmlStreamWriter.writeStartElement("B");
        xmlStreamWriter.writeEndElement(); // end of B
        xmlStreamWriter.writeCharacters("\n");

        // <C/>
        xmlStreamWriter.writeCharacters("   ");
        xmlStreamWriter.writeEmptyElement("C");
        xmlStreamWriter.writeCharacters("\n");

    // </Root>
    xmlStreamWriter.writeEndElement(); // end of Root

    xmlStreamWriter.writeEndDocument();
} finally {
    xmlStreamWriter.close();
}

<强>输出:

<?xml version="1.0" ?>
<Root Path="...">
   <A x="abc">Hello World</A>
   <B></B>
   <C/>
</Root>