簡體   English   中英

如何在Java中使用StAX讀取XML的修改片段?

[英]How to read modify fragments of XML using StAX in Java?

我的目標是將對象(featureMember)讀入DOM,對其進行更改,然后寫回新的XML。 XML太大,無法使用DOM本身。 我知道我需要的是StAX和TransformerFactory,但是我無法使其正常運行。

這是我到目前為止所做的:

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()上):

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

我的xml的簡化版本看起來像(它具有名稱空間):

<?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>

我有一個要更改的對象(featureMember)的localId列表,對應於已更改的RST或pole(取決於用戶更改了哪個):

localId(id1234)RST(1001)

localId(id1236)RST(1003)

...

您遇到的問題是,當您創建StAXSource ,您的START_ELEMENT事件已被使用。 因此, XMLEventReader可能在某個空白文本節點事件中,或者在其他不能作為XML文檔源的事件中發生。 您可以使用peek()方法查看下一個事件,而無需使用它。 但是,請確保首先使用hasNext()進行事件。

我不確定要完成的目標100%,因此您可以根據具體情況執行以下操作。

編輯:我只是讀了一些關於您的問題的評論,這使事情變得更加清楚。 下面的內容仍然可以通過一些調整幫助您達到所需的結果。 還要注意,Java XSLT處理器允許擴展功能和擴展元素,它們可以從XSLT樣式表調用Java代碼。 這是一種使用外部資源(例如數據庫查詢)擴展基本XSLT功能的強大方法。


如果希望將輸入XML轉換為一個輸出XML,則最好只使用XML樣式表轉換。 在您的代碼中,您將創建一個沒有任何模板的轉換器,因此它成為默認的“身份轉換器”,僅將輸入復制到輸出。 假設您的輸入XML如下:

<?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前綴綁定到一些虛擬名稱空間,因為您的示例中缺少該名稱空間,並修復了格式錯誤的RST元素。

以下程序將對您的輸入運行XSLT轉換,並將其寫入輸出文件。

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和類相同的包中。

<?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可以做什么的一個非常簡單的示例。

輸出為:

<?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非常快速高效,因此就足夠了。


也許您實際上是想將某個元素的每次出現都拆分為自己的輸出文件,並對它進行一些更改。 這是一個使用StAX將<gml:featureMember>元素拆分為單獨文檔的代碼示例。 然后,您可以遍歷創建的文件,然后根據需要對其進行轉換(XSLT還是一個不錯的選擇)。 顯然,錯誤處理將需要更強大。 這只是為了演示。

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;
                }
            }
        }

    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM