# Serialisieren mehrerer Klassen scheitert - warum?



## J-C (13. Mai 2009)

Hallo,

ich habe 3 Klassen, die alle miteinander Verbunden sind:

Klasse 1 - Netz - enthält eine Hashmap<String, Knoten> aller Knoten mit einem Schlüßel

Klasse 2 - Halte- enthält eine Hashmap<Halte, Kante> aller verbundenen Knoten und Kanten, die die Kante genauer beschreiben

Klasse 3 - Kanten - enthält unter anderem die entfernung + weitere attribute

Wenn ich das Netz serialisieren will, erhalte ich folgende Exceptions


```
Exception in thread "main" java.lang.StackOverflowError
    at sun.reflect.GeneratedMethodAccessor6.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
    at java.util.HashMap.writeObject(HashMap.java:1001)
    at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
    at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:416)
    at mapapp.datatypes.DtHalt.writeObject(DtHalt.java:86)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
    at 

So ich kürz das hier mal ab, obwohl noch ca. 60.000 Zeichen kommen ;)
```

Was mache ich falsch?

Es funktioniert, wenn ich in der Klasse Halte nicht die verbunden Halte sondern nur deren Schlüßel speichere... nur ist das uneffektiv, da ich bei jedem zugriff dann die Halte erst wieder aus der HashMap des Netzes holen muss. 

Meine Vermutung: Irgendwie kommt er damit nicht klar eine simple Referenz auf das Objekt Halt, das ja schon im Netz serialisiert wurde zu speichern, wenn die verbundenen Knoten gespeichert werden sollen.

Für Hilfe wäre ich sehr dankbar


----------



## zeja (13. Mai 2009)

In deinem Stacktrace solltest du dann ja sehen dass irgendeine Methode immer wieder aufgerufen wird.

Serialisierung mit Java ist vielleicht nicht die richtige Wahl. XStream bietet die Möglichkeit Objekte nach XML zu speichern (und von da zu laden) und unterstützt auch die Referenzierung wenn ein Objekt mehrfach vorkommt. Dafür musst du nur etwa 3 Zeilen Code schreiben.


----------



## J-C (13. Mai 2009)

Mit folgender Zeile endet immer die Exception und fängt wieder an.


```
at java.util.HashMap.writeObject(HashMap.java:1001)
```

Ich werde mir XStream mal anschauen. Obwohl ich denke, dass es eigentlich auch einfacher gehen müsste...


----------



## zeja (13. Mai 2009)

Einfacher gehts nicht als XStream.


----------



## J-C (13. Mai 2009)

Also bis jetzt gefällt mir XStream - nur sind die Dateien wirklich sehr groß Eigentlich schon zu groß ...


----------



## J-C (13. Mai 2009)

Vieeel zu groß!

mittels java serialisierung sind es gute 3mb 
mit Xstream sind es knapp 30mb - irgendwie ist das nicht der richtige Weg


----------



## zeja (13. Mai 2009)

Na du kannst sie ja noch wesentlich kleiner kriegen, indem du XStream noch ein wenig konfigurierst. Wenn die Elementnamen kleiner sind, dann ist die Datei auch wesentlich kleiner. Einrückungen kann man z.B. auch weglassen. Welchen Modus hast du denn verwendet? Je nachdem wirds auch größer oder kleiner.

Andere Möglichkeit: Einmal zippen danach. Dann sollte es auch schön klein sein.


----------



## J-C (13. Mai 2009)

Ich denke das Problem ist folgendes

Halt A ist verbunden mit Halt B und Halt B ist mit Halt C verbunden und Halt C wieder mit A ... und so versucht er die Objekte im Kreis immer nacheinander zu serialisieren...

oder irre ich mich?


----------



## zeja (13. Mai 2009)

Beim Serialisieren ja.

XStream sollte das entsprechend merken. Wie hast du es denn konfiguriert? Poste doch mal den Code.


----------



## J-C (13. Mai 2009)

ich hab nix weiter gemacht... 
xstream objekt
file und buffered writer und ab mit der toXML Funktion


----------



## zeja (13. Mai 2009)

Dann lies dir die Dokumentation durch und stell es nach deinen Bedürfnissen ein.


----------



## Thomas Darimont (13. Mai 2009)

Hallo,

mach doch mal ein kleines mini Beispiel, dass dein Problem demonstriert.

Gruß Tom


----------



## J-C (14. Mai 2009)

Also 1. Xstream scheitert auch  und kann die Klasse nicht serialisieren:

Ich versuch jetzt nochmal ein Beispiel:

Wir haben einen mit zyklen behafteten Graphen, den ich durch folgende 3 Klassen abbilde:


```
public class DtNetz implements Serializable {

    private static final long serialVersionUID = 1L;
    
    private HashMap<String, DtHalt> haltlist = new HashMap<String, DtHalt>();
    //Der Stirng ist eine "ID"....
    
    public DtNetz() {

    }    
}
```

Hier die Knoten

```
public class DtHalt implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private String attribut_1;
    private String attribut_2;
    private String attribut_3;
    
    private transient GeoPosition geoPosition;
    
    private HashMap<DtHalt, DtKante> hm_connected_to = new HashMap<DtHalt, DtKante>();
    
    public DtHalt() {        
    }
}
```

und noch die Kanten


```
public class DtKante implements Serializable {
    
    private Integer distance = null;
    
    private ArrayList<String> al_1 = new ArrayList();
    
    public void DtKante() {
    }

}
```

Wenn ich diesen Graphen befülle... ca 16.000 Knoten, dann kann ich das ganze nicht serialisieren.... Ich serialisiere natürlich nur das Netz und Java hangelt sich ja dann automatisch durch.
Und bei den Halten wird dann irgendwie immer im Kreis gerannt oder so... ich hab keine Ahnung!
Jedenfalls kann XStream diesen Graphen auch nicht serialisieren


----------



## Thomas Darimont (14. Mai 2009)

Hallo,

ich meinte eigentlich, dass du ein minimales lauffähiges Beispiel angeben solltest.

Ansonsten würde ich dir empfehlen den Graphen in einer anderen Form beispielsweise als Adjazenz Liste / Matrix abzuspeichern.

Wobei, es könnte sein das du selber einen Serialisierungs-Bug eingebaut hast... hast du eigene writeObject, writeReplace Methoden implementiert?

Gruß Tom


----------



## zeja (14. Mai 2009)

XStream kann von sich aus bei entsprechender Konfiguration komplexe Graphen serialisieren. Das habe ich in meiner Diplomarbeit so verwendet. Ich weiß ehrlich gesagt nicht wo das Problem ist einmal die Dokumentation zu lesen und die entsprechenden Einstellungen vorzunehmen.


----------



## J-C (14. Mai 2009)

OK - diese Seite habe ich gefunden:

http://xstream.codehaus.org/graphs.html

gut - denke das hat mit meinem Thema zu tun. 

Nur leider funktioniert es - egal welche Einstellung - NICHT 


```
at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:157)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:148)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.visit(AbstractReflectionConverter.java:118)
    at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:129)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:100)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:58)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:63)
    at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.writeItem(AbstractCollectionConverter.java:64)
    at com.thoughtworks.xstream.converters.collections.MapConverter.marshal(MapConverter.java:57)
    at com.thoughtworks.xstream.core.AbstractReferenceMarshaller.convert(AbstractReferenceMarshaller.java:68)
    at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:157)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:148)
    at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.visit(AbstractReflectionConverter.java:118)
    at
```

Vielleicht wärst du zeja ja so gnädig und würdest mir helfen - da du es ja schon mal offensichtlich gemacht hast.

Was habe ich bis jetzt konfiguriert... hmm naja

```
fos = new FileOutputStream("Netz.xml");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            XStream xstream = new XStream();
            xstream.setMode(XStream.XPATH_ABSOLUTE_REFERENCES);
            xstream.toXML(hm_Halte, bos);
```
so lasse ich das in eine Datei schreiben... ich habe alle REFERENZ-Modelle durchprobiert, aber bringt irgendwie nix.

Ich weiss auch nicht, was du mit Konfigurieren meinst - überall auf der Seite steht im wesentlichen, dass XStream alles out of the box kann. Wenn du mir nur einen Tipp geben könntest, zu welchem Thema ich weitersuchen sollte - einfach so in der API Blättern halte ich für uneffektiv!


----------



## zeja (14. Mai 2009)

Vielleicht ist bei dir noch was anders, aber probier das mal:


```
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.thoughtworks.xstream.XStream;

public class XStreamTest {

	public static void main(String[] args) throws IOException {
		final Graph graph = new Graph();

		final Node a = graph.addNode("a");
		final Node b = graph.addNode("b");
		final Node c = graph.addNode("c");

		new Edge(a, b);
		new Edge(b, c);
		new Edge(c, a);

		final FileOutputStream fos = new FileOutputStream("Netz.xml");
		final BufferedOutputStream bos = new BufferedOutputStream(fos);
		final XStream xstream = new XStream();
		xstream.alias("edge", Edge.class);
		xstream.alias("node", Node.class);
		xstream.alias("graph", Graph.class);
		xstream.setMode(XStream.XPATH_ABSOLUTE_REFERENCES);
		xstream.toXML(graph, bos);
		bos.close();
	}

	public static final class Graph {

		private final Map<String, Node> graph = new HashMap<String, Node>();

		public Node addNode(String name) {
			final Node node = new Node(name);
			graph.put(name, node);
			return node;
		}
	}

	public static final class Edge {

		private final Node inNode;
		private final Node outNode;

		public Edge(final Node inNode, final Node outNode) {
			this.inNode = inNode;
			this.outNode = outNode;

			inNode.addOutEdge(this);
			outNode.addInEdge(this);
		}

	}

	public static final class Node {

		private final List<Edge> inEdges = new ArrayList<Edge>();
		private final List<Edge> outEdges = new ArrayList<Edge>();

		private final String name;

		public Node(String name) {
			this.name = name;
		}

		public final void addInEdge(Edge edge) {
			this.inEdges.add(edge);
		}

		public final void addOutEdge(Edge edge) {
			this.outEdges.add(edge);
		}

	}
}
```


----------



## J-C (14. Mai 2009)

Danke - jetzt hab ichs


----------

