如何在Java中使用StAX读取XML的修改片段? [英] How to read modify fragments of XML using StAX in Java?

查看:81
本文介绍了如何在Java中使用StAX读取XML的修改片段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是将对象(featureMember)读入DOM,对其进行更改并写回新的XML。 XML太大,无法使用DOM本身。我知道我需要的是StAX和TransformerFactory,但我无法使其工作。

My goal is to read objects (featureMember) into DOM, change them and write back into new XML. XML is too big to use DOM itself. I figured what I need is StAX and TransformerFactory, but I can't make it work.

这是我到目前为止所做的:

This is what I've done till now:

private void change(File pathIn, File pathOut) {
    try {

        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLOutputFactory factoryOut = XMLOutputFactory.newInstance();

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();

        XMLEventReader in = factory.createXMLEventReader(new FileReader(pathIn));
        XMLEventWriter out = factoryOut.createXMLEventWriter(new FileWriter(pathOut));

        while (in.hasNext()) {
            XMLEvent e = in.nextTag();
            if (e.getEventType() == XMLStreamConstants.START_ELEMENT) {
                if (((StartElement) e).getName().getLocalPart().equals("featureMember")) {
                    DOMResult result = new DOMResult();
                    t.transform(new StAXSource(in), result);
                    Node domNode = result.getNode();
                    System.out.println(domnode);
                }
            }
            out.add(e);
        }
        in.close();
        out.close();

    } catch (FileNotFoundException e1) {
        e1.printStackTrace();
    } catch (IOException e1) {
        e1.printStackTrace();
    } catch (TransformerConfigurationException e1) {
        e1.printStackTrace();
    } catch (XMLStreamException e1) {
        e1.printStackTrace();
    } catch (TransformerException e1) {
        e1.printStackTrace();
    }
}

我收到异常(在t.transform()上) :

I get exception (on t.transform()):

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: StAXSource(XMLEventReader) with XMLEventReader not in XMLStreamConstants.START_DOCUMENT or XMLStreamConstants.START_ELEMENT state

我的xml的简化版本看起来像(它具有名称空间):

Simplified version of my xml looks like (it has namespaces):

<?xml version="1.0" encoding="UTF-8"?>
<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="featureCollection">
  <gml:featureMember>
    </eg:RST>
    <eg:pole>Krakow</eg:pole>
    <eg:localId>id1234</eg:localId>
  </gml:featureMember>
  <gml:featureMember>
    <eg:RST>1002</eg:RST>
    <eg:pole>Rzeszow</eg:pole>
    <eg:localId>id1235</eg:localId>
  </gml:featureMember>
</gml:FeatureCollection>

我有一个对象的localId列表(featureMember),我想对其进行更改并相应地更改了RST或极点(取决于用户更改了哪个):

I have a list of localId's of objects (featureMember), which I want to change and correspoding changed RST or pole (it depends on user which one is changed):

localId(id1234)RST(1001)

localId (id1234) RST (1001)

localId(id1236)RST(1003)

localId (id1236) RST (1003)

...

推荐答案

您遇到的问题是,当您创建 StAXSource 时,您的START_ELEMENT事件已被使用。因此, XMLEventReader 可能是在某些空白文本节点事件中发生的,或者是其他不能作为XML文档源的事件。您可以使用 peek()方法查看下一个事件,而无需使用它。不过,请确保先有一个事件 hasNext()

The problem you're having is that when you create the StAXSource, your START_ELEMENT event has already been consumed. So the XMLEventReader is probably at some whitespace text node event, or something else that can't be an XML document source. You can use the peek() method to view the next event without consuming it. Make sure there is an event with hasNext() first, though.

我不确定100%您希望完成的工作,因此,根据情况,可以执行一些操作。

I'm not 100% sure of what you wish to accomplish, so here's some things you could do depending on the scenario.

编辑:我只是读了一些关于您问题的评论,这使事情有点更清晰。下面的内容仍然可以通过一些调整帮助您实现所需的结果。还要注意,Java XSLT处理器允许使用扩展功能和扩展元素,它们可以从XSLT样式表调用Java代码。这是一种使用外部资源(例如数据库查询)扩展基本XSLT功能的强大方法。

I just read some of the comments on your question which make things a bit more clear. The below could still help you to achieve the desired result with some adjustment. Also note that Java XSLT processors allow for extension functions and extension elements, which can call into Java code from an XSLT stylesheet. This can be a powerful method to extend basic XSLT functionality with external resources such as database queries.

将输入XML转换为一个输出XML,最好使用XML样式表转换。在您的代码中,您将创建一个没有任何模板的转换器,因此它成为默认的身份转换器,仅将输入复制到输出。假设您输入的XML如下:

In case you want the input XML to be transformed into one output XML, you might be better of simply using an XML stylesheet transformation. In your code, you create a transformer without any templates, so it becomes the default "identity transformer" which just copies input to output. Suppose your input XML is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="featureCollection" xmlns:eg="acme.com">
  <gml:featureMember>
    <eg:RST/>
    <eg:pole>Krakow</eg:pole>
    <eg:localId>id1234</eg:localId>
  </gml:featureMember>
  <gml:featureMember>
    <eg:RST>1002</eg:RST>
    <eg:pole>Rzeszow</eg:pole>
    <eg:localId>id1235</eg:localId>
  </gml:featureMember>
</gml:FeatureCollection>

我已经将 eg 前缀绑定到

以下程序将对您的输入运行XSLT转换并将其写入输出文件

The following program will run an XSLT transformation on your input and writes it to an output file.

package xsltplayground;

import java.io.File;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class XSLTplayground {

    public static void main(String[] args) throws Exception {

        URL url = XSLTplayground.class.getResource("sample.xml");
        File input = new File(url.toURI());
        URL url2 = XSLTplayground.class.getResource("stylesheet.xsl");
        File xslt = new File(url2.toURI());
        URL url3 = XSLTplayground.class.getResource(".");
        File output = new File(new File(url3.toURI()), "output.xml");
        change(input, output, xslt);

    }

    private static void change(File pathIn, File pathOut, File xsltFile) {
        try {

            // Creating transformer with XSLT file
            TransformerFactory tf = TransformerFactory.newInstance();
            Source xsltSource = new StreamSource(xsltFile);
            Transformer t = tf.newTransformer(xsltSource);

            // Input source
            Source input = new StreamSource(pathIn);

            // Output target
            Result output = new StreamResult(pathOut);

            // Transforming
            t.transform(input, output);

        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

}

示例stylesheet.xsl文件,为方便起见,我将其与输入XML和类一起转储到同一包中。

Here's a sample stylesheet.xsl file, which for convenience I just dumped into the same package as the input XML and class.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:eg="acme.com">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="gml:featureMember">
        <gml:member>
            <xsl:apply-templates select="node()|@*" />
        </gml:member>
    </xsl:template>

</xsl:stylesheet>

上面的样式表将默认复制所有内容,但当复制到时< gml:featureMember> 元素,它将内容包装到新的< gml:member> 元素中。仅仅是使用XSLT可以做什么的一个非常简单的示例。

The above stylesheet will copy everything by default, but when it gets to a <gml:featureMember> element it will wrap the contents into a new <gml:member> element. Just a very simple example of what you can do with XSLT.

输出将是:

<?xml version="1.0" encoding="UTF-8"?>
<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:eg="acme.com" gml:id="featureCollection">
  <gml:member>
    <eg:RST/>
    <eg:pole>Krakow</eg:pole>
    <eg:localId>id1234</eg:localId>
  </gml:member>
  <gml:member>
    <eg:RST>1002</eg:RST>
    <eg:pole>Rzeszow</eg:pole>
    <eg:localId>id1235</eg:localId>
  </gml:member>
</gml:FeatureCollection>

由于输入和输出都是文件流,因此不需要内存中的整个DOM。 Java中的XSLT非常快速且高效,因此这可能就足够了。

Since both input and output are file streams, you don't need the entire DOM in memory. XSLT in Java is pretty fast and efficient, so this might suffice.

也许您实际上想将每次出现的次数都分成几部分元素放入自己的输出文件中,并对其进行了一些更改。这是一个使用StAX分离< gml:featureMember> 元素作为单独文档的代码示例。然后,您可以遍历创建的文件,然后根据需要进行转换(XSLT仍然是一个不错的选择)。显然,错误处理将需要更强大。

Maybe you actually want to split every occurrence of some element into its own output file, with some changes to it. Here's an example of code that uses StAX for splitting off the <gml:featureMember> elements as separate documents. You could then iterate over the created files an transform them however you want (XSLT would again be a good choice). Obviously the error handling would need to be a bit more robust. This is just for demonstration.

package xsltplayground;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;

public class XSLTplayground {

    public static void main(String[] args) throws Exception {

        URL url = XSLTplayground.class.getResource("sample.xml");
        File input = new File(url.toURI());
        URL url2 = XSLTplayground.class.getResource("stylesheet.xsl");
        File xslt = new File(url2.toURI());
        URL url3 = XSLTplayground.class.getResource(".");
        File output = new File(url3.toURI());
        change(input, output, xslt);

    }

    private static void change(File pathIn, File directoryOut, File xsltFile) throws InterruptedException {
        try {

            // Creating a StAX event reader from the input
            XMLInputFactory xmlIf = XMLInputFactory.newFactory();
            XMLEventReader reader = xmlIf.createXMLEventReader(new StreamSource(pathIn));

            // Create a StAX output factory
            XMLOutputFactory xmlOf = XMLOutputFactory.newInstance();

            int counter = 1;
            // Keep going until no more events
            while (reader.hasNext()) {
                // Peek into the next event to find out what it is
                XMLEvent next = reader.peek();
                // If it's the start of a featureMember element, commence output
                if (next.isStartElement() 
                        && next.asStartElement().getName().getLocalPart().equals("featureMember")) {
                    File output = new File(directoryOut, "output_" + counter + ".xml");
                    try (OutputStream ops = new FileOutputStream(output)) {
                        XMLEventWriter writer = xmlOf.createXMLEventWriter(ops);
                        copy(reader, writer);
                        writer.flush();
                        writer.close();
                    }
                    counter++;
                } else {
                    // Not in a featureMember element: ignore
                    reader.next();
                }
            }

        } catch (XMLStreamException ex) {
            Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }

    private static void copy(XMLEventReader reader, XMLEventWriter writer) throws XMLStreamException {

        // Creating an XMLEventFactory
        XMLEventFactory ef = XMLEventFactory.newFactory();
        // Writing an XML document start
        writer.add(ef.createStartDocument());

        int depth = 0;
        boolean stop = false;
        while (!stop) {
            XMLEvent next = reader.nextEvent();
            writer.add(next);
            if (next.isStartElement()) {
                depth++;
            } else if (next.isEndElement()) {
                depth--;
                if (depth == 0) {
                    writer.add(ef.createEndDocument());
                    stop = true;
                }
            }
        }

    }

}

这篇关于如何在Java中使用StAX读取XML的修改片段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆