# XML Stream unterbrechen und wiederaufnehmen



## basteln (23. Mrz 2015)

Hallo Leute,

ich sitze gerade an einem Projekt in welchem ich Xml-Files mit 50k+ Zeilen erstellen soll. Teilweise muss ich diese Strukturen generieren und teilweise kann ich diese auch aus vorhandenen Files (Pattern) einfügen.

Ich nutze zum Schreiben der Datei einen XMLStreamWriter. Meine Frage ist nun Folgende: Wie kann ich nach dem Schließen des Streams und dem Hinzufügen der schon vorhandenen Elemente aus einem anderen xml-File, besagten Stream ab Ende des Dokuments wiederaufnehmen?

Vielen Dank.

mfg
basteln


----------



## Flown (23. Mrz 2015)

Nein so einfach geht das nicht, denn XMLStreamWriter hat einen internen State der die öffnenden und schließenden Tags mitverfolgt.


----------



## basteln (23. Mrz 2015)

Ok. Die sicherlich naheliegendste Folgefrage, sofern nicht schon oben gestellt, wäre nun also: Wie geht's?

Gruß


----------



## Flown (23. Mrz 2015)

Gib mal ein kleines Beispiel wie du was machst. Also was sind deine Inputdaten und was gibst du aus und wie machst du es?


----------



## basteln (23. Mrz 2015)

Gut, hier mal eine grobe Beschreibung:
Ich bekomme meine Daten aus einer Datenbank und fülle mit diesen entsprechende Objekte. Diese Objekte werden in einer Art Handler (Kontainerklasse) gespeichert und können von diesem zurückgegeben werden. Der Content, welcher zusätzlich eingefügt werden soll liegt in xml-Files mit einer gültigen xml-Struktur. Sprich ich muss an bestimmten Stellen diese xml-Daten einbauen und danach ggf. wieder Content mit Hilfe der Datenbankdaten generieren können.
Zusätzlich habe ich eine Klasse, welche den Stream öffnen und schließen kann (open(), close()). Die open() schaut ugf. so aus:


```
File file = new File(xmlPath);

if (file.exists())
            return false; // TODO

// try to create the writer
try
{
// define a new printwriter
writer = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(file)));

// define XMLEventWriter and XMLStreamWriter factory instance
XMLOutputFactory xof = XMLOutputFactory.newInstance();
// only one of the following is required.
xmlsw = xof.createXMLStreamWriter(writer);
} catch (FileNotFoundException e)
{
    return false; //TODO
} catch (XMLStreamException e)
{
    return false;
}

return true;
```
Eigentlich wird am Ende der Writer zurückgegeben, ich hab es ein wenig abgewandelt.
In der Klasse die diese Funktion aufruft, wird nun mit dem Parsen begonnen.

Die Strucktur der xml schaut bildlich geschrieben wie folgt aus:

```
<Oberelement>
<Value>
<viel zu generierender Content>
<einzufügender Content>
<zu generierender Content>
...
</Value>
</Oberelement>
```
Diese File ist min. ~40k Zeilen lang, wenn es weitere bestimmte Elemente gibt, erweitert sich dieser Content um rund 10k Zeilen, die eingefügt werden müssen.


----------



## Flown (23. Mrz 2015)

Du musst auf jedenfall den gleichen XMLOutputStream verwenden, wenn du es unbedingt mit StaX machen möchtest (eben wegen den internen States). Das heißt aber du musst 1:1 die eingelesene XML durchschleusen, dass wiederum ist kein Problem (ist ein wenig ein aufwand, aber schnell geschrieben). 

Oder sehe ich da was falsch?


----------



## basteln (23. Mrz 2015)

Ich muss gestehen, dass ich noch nicht so die mega Erfahrung mit xml habe. Durch diverse Tutorials bin ich auf staX gestoßen und habe es halt verwendet. Sofern es geht, wäre es super, wenn ich das Eingelesene direkt mit einschleusen könnte.

Nur nochmal zum Verständnis:
1. Ich öffne den FileOutputStream (oder meinst du die XMLOutputFactory?) und speichere ihn in einer Variable.
2. Ich schreibe mit meinem XMLStreamwriter meine zu generierenden Blöcke
- an den Stellen, wo ich nun ein anderes Xml-File einbinden möchte -
3. nehme ich diesen Outputstream, um das eingelesene zu schreiben
- und kann danach -
4. ganz normal mit dem Streamwriter weitermachen?


----------



## Flown (23. Mrz 2015)

So hab mal ein kleines Programm für dich zusammengestellt (ist jetzt aber die Iterator API). Es liest eine XML-String in meinem Fall ein und fügt es dem bestehendem XML hinzu.



Spoiler: code





```
package test;

import java.io.StringReader;
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.events.XMLEvent;

public class Test {

  public static void main(String... args) throws Exception {

    String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
            + "<root>\n"
            + "  <container>\n"
            + "    <value attr=\"1\" />\n"
            + "    <value attr=\"2\" />\n"
            + "  </container>\n"
            + "</root>";
    XMLOutputFactory factory = XMLOutputFactory.newInstance();
    XMLEventWriter writer = factory.createXMLEventWriter(System.out);

    XMLEventFactory ef = XMLEventFactory.newInstance();

    writer.add(ef.createStartDocument());
    writer.add(ef.createStartElement("", "", "mycontainer"));

    XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(new StringReader(xml));
    while (reader.hasNext()) {
      XMLEvent event = reader.nextEvent();
      if (!(event.isStartDocument() || event.isEndDocument())) {
        writer.add(event);
      }
    }
    writer.add(ef.createEndElement("", "", "mycontainer"));
    writer.add(ef.createEndDocument());
    writer.flush();
  }
}
```


----------



## basteln (24. Mrz 2015)

Hallo,

vielen Dank für dieses Schnippsel. Wenn ich das bei mir nun so einbaue, muss ich quasi meinen XMLStreamwriter durch die XMLEventFactory ersetzen. Zudem muss ich diese Anweisungen ein wenig abändern:

```
xmlsw.writeStartElement("Descriptor");
// zu
ef.createStartElement("", "", "Descriptor")
```
Wenn ich dann das einzufügende Element aus einer Datei lese, wird dies ja Zeile für Zeile geschehen. Da kann ich dann quasi in der Einleseschleife auch gleichzeitig wieder schreiben, richtig?

Grüße


----------



## Flown (24. Mrz 2015)

Ja richtig so wie in meinem Code.

Du könntest sogar einen ganzen XMLEventReader hinzufügen und er schreibt dir automatisch das ganze Dokument in deinen Output: HIER. Aber das kannst du nur tun, wenn dein XML nicht wohlgeformt ist. D.h. am anfang die XML Deklaration hat (<?xml version="1.0" encoding="UTF-8"?>). Das fange ich ab und lasse es weg und füge nur den Content hinzu.


----------



## basteln (24. Mrz 2015)

Das ist ja super,
mein XML ist nicht wohlgeformt, weil es durch ein spezielles Programm wieder eingelesen werden muss und dieses keinen xml-Header erwartet 

Besten Dank, ich muss jetzt ein paar 1000 Zeilen ändern, aber das ist nicht so schwer. Das nächste Mal beschäftige ich eher mit solchen Problematiken.

mfg
basteln


----------



## basteln (24. Mrz 2015)

Sry für den Doppelpost, ich kann aber gerade nicht mehr editieren.

Ich habe das Schreiben des Files in mehrere Methoden unterteilt. Diesen übergebe ich die Factory und den Writer. Muss ich beide auch wieder zurückgeben, damit diese irgendwelche Counter richtig weiterzählen, oder ist das hier nicht mehr nötig?

Grüße


----------



## Flown (24. Mrz 2015)

Du übergibst schätze ich nur Referenzen, also ist das zurückgeben nicht nötig.


----------



## basteln (16. Apr 2015)

Hallo,

ich hebe diesen Thread nochmal aus. 
Ich habe das oben stehende umgesetzt und bin nun auf der Fehlersuche. Mein Programm wirft beim Schreiben eine Exception bei der ich nicht weiß, was zu tun ist.

>> javax.xml.stream.XMLStreamException: No element was found to write: java.lang.ArrayIndexOutOfBoundsException: -1


```
writer.add(ef.createStartElement("", "", "One2OneMapping"));
// insert pattern
reader.insertPattern(writer);
writer.add(ef.createEndElement("", "", "One2OneMapping"));
```

geworfen wird sie beim EndElement.

Habt ihr eine Ahnung was der von mir will?

Liebe Grüße

Edit: alles vorher kann problemlos geschrieben werden, auch das Einfügen an an vorherigen Stellen bisher problemlos funktioniert.


----------



## basteln (17. Apr 2015)

Da man hier nicht mehr editieren kann ein Doppelpost.
Hier mal mein reader:


```
public void insertPattern(XMLEventWriter writer)
            throws FileNotFoundException, XMLStreamException,
            FactoryConfigurationError
{
            String path = Constants.PATH_SEM_GA_PATTERN;

            // create a new file
            File file = new File(path);

            // instantiate the reader
            reader = XMLInputFactory.newInstance().createXMLEventReader(
                         new FileInputStream(file));
            XMLEvent event;

            // read the file
            while (reader.hasNext())
            {
                        // insert every single line into the xml file
                        event = reader.nextEvent();
                        if(!event.isStartDocument())
                                    /writer.add(event);
            }

            // close the reader
             reader.close();
    }
```

Ist das normal, dass die Eventfactory location = null ist?


----------



## Flown (17. Apr 2015)

Also so einfach kann man das jetzt nicht sagen, was es da hat. Aber wie du schon in deine Exception herauslesen kannst, gibt es kein zu schreibendes Event. Zusätzlich wirft dir die Exception eine Zeilennummer dabei aus, wo der Fehler auftritt. Da musst du dann ansetzen. Von dieser Seite aus kann ich/können wir dir nicht helfen.


----------



## basteln (17. Apr 2015)

Ich habe eben kurz das Element aus dem Context geholt und in einem kleinen Test eingefügt. Auch da kommt dieser Fehler:

Testklasse:

```
public void test()
    {
        String xmlPath = System.getProperty("user.dir") + "\\test.xml";
        XmlReader reader = new XmlReader();
        if(open(xmlPath).equals(Constants.STATUS_OK))
        {
            try
            {
                writer.add(eventFactory.createStartElement("", "", "BLA"));
                reader.insertSemGaPattern(writer);
                writer.add(eventFactory.createEndElement("", "", "BLA"));
                writer.close();
            } catch (XMLStreamException | FileNotFoundException | FactoryConfigurationError e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
    }
```

und der Reader ist ja oben schon beschrieben. Der Fehler kommt, wie oben auch, in dieser Zeile:
writer.add(eventFactory.createEndElement("", "", "BLA"));

EDIT:
Beim Einlesen scheint er mitten drinne abzubrechen:
</Name><Type>System.Int32<


----------



## Flown (17. Apr 2015)

Wenn du mir einen Testcode also ein kleines kompilierbares Beispiel mit Testdaten lieferst, dann kann ich dir helfen. So leider nicht!


----------



## basteln (17. Apr 2015)

Gut ich habe für das Forum schnell ein kleines Beispiel geschrieben:


```
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

import javax.xml.stream.FactoryConfigurationError;
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;

public class ForumTest
{
    private XMLEventWriter  writer;
    private XMLEventFactory eventFactory;

    private boolean open(String xmlPath)
    {
        File file = new File(xmlPath);

        try
        {
            XMLOutputFactory factory = XMLOutputFactory.newInstance();
            writer = factory.createXMLEventWriter(new OutputStreamWriter(
                    new FileOutputStream(file)));
            eventFactory = XMLEventFactory.newInstance();

        } catch (FileNotFoundException e)
        {
            e.printStackTrace();

        } catch (XMLStreamException e)
        {
            e.printStackTrace();
        }

        return true;
    }

    private boolean close()
    {
        // flush and close
        try
        {
            writer.flush();
            writer.close();
        } catch (XMLStreamException e)
        {
            e.printStackTrace();
        }

        return true;
    }
    
    private void read(XMLEventWriter writer)
    {
        String pathname = System.getProperty("user.dir") + "\\patterntest.xml";
        File file = new File(pathname);
        try
        {
            XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(
                    new FileInputStream(file));
            XMLEvent event;
            while(reader.hasNext())
            {
                event = reader.nextEvent();
                writer.add(event);
            }
            reader.close();
        } catch (FileNotFoundException | XMLStreamException
                | FactoryConfigurationError e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void testLoadData()
    {
        String xmlPath = System.getProperty("user.dir") + "\\test.xml";
        if (open(xmlPath))
        {
            try
            {
                writer.add(eventFactory.createStartElement("", "", "BLA"));
                read(writer);
                writer.add(eventFactory.createEndElement("", "", "BLA"));
                close();
                System.out.println("fertsch");
            } catch (XMLStreamException | FactoryConfigurationError e)
            {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args)
    {
        ForumTest test = new ForumTest();
        test.testLoadData();
    }
}
```

In der patterntest.xml steht das Folgende:
<Test><Name>Test</Name><Type>System.Int32</Type><Value>12</Value></Test>

Exception wird geworfen...


----------



## Flown (17. Apr 2015)

Das war etwas gemein muss ich sagen. Du musst bei deinem Kopieren aufpassen, dass auch kein endDocument-Event daherrauscht, sonst schließt er noch alle Tags die offen sind automatisch.

So hier hab ich dir mal den Code ein wenig verändert & ausgebessert:


```
import java.io.OutputStreamWriter;
import java.io.StringReader;

import javax.xml.stream.FactoryConfigurationError;
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;

public class ForumTest {
  private XMLEventWriter writer;
  private XMLEventFactory eventFactory;
  
  private boolean open(String xmlPath) {
    try {
      XMLOutputFactory factory = XMLOutputFactory.newInstance();
      writer = factory.createXMLEventWriter(new OutputStreamWriter(System.out));
      eventFactory = XMLEventFactory.newInstance();
      
    } catch (XMLStreamException e) {
      e.printStackTrace();
    }
    return true;
  }
  
  private boolean close() {
    try {
      writer.flush();
      writer.close();
    } catch (XMLStreamException e) {
      e.printStackTrace();
    }
    return true;
  }
  
  private void read() {
    try {
      XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(
          new StringReader("<Test><Name>Test</Name><Type>System.Int32</Type><Value>12</Value></Test>"));
      while (reader.hasNext()) {
        XMLEvent e = reader.nextEvent();
        if (!(e.isStartDocument() || e.isEndDocument())) {
          writer.add(e);
        }
      }
      reader.close();
      writer.flush();
    } catch (XMLStreamException | FactoryConfigurationError e) {
      e.printStackTrace();
    }
  }
  
  public void testLoadData() {
    if (open("")) {
      try {
        writer.add(eventFactory.createStartElement("", "", "BLA"));
        read();
        read();
        writer.add(eventFactory.createEndElement("", "", "BLA"));
        close();
        System.out.println("\nfertsch");
      } catch (FactoryConfigurationError | XMLStreamException e) {
        e.printStackTrace();
      }
      
    }
  }
  
  public static void main(String[] args) {
    ForumTest test = new ForumTest();
    test.testLoadData();
  }
}
```


----------



## basteln (17. Apr 2015)

Ach mensch. Daran hab ich garnicht gedacht 
Holla die Waldfee, vielen Dank!


----------

