# Serialisiertes Objekt innerhalb einer XML Node



## peez (9. Feb 2009)

Ich möchte gerne ein XML speichern, in dem innerhalb einer bestimmten Node ein serialisiertes Objekt gespeichert wird.
Also so ähnlich:


```
<Rootnode>
  <Datensatz>
    <Name>Hanspeter</Name>
    <Objekt>#0,/=§;java.util.Hashtable.)/&=§Kjok39/§=§  (oder wie das serialisierte objekt eben aussieht)</Objekt>
  </Datensatz>
</Rootnode>
```

Schreiben scheint zu funktionieren. Wenn ich das XML dann allerdings wieder lesen möchte, bekomme ich beim Parsen eine MalformedSequenceException (Invalid byte 1 of 1-byte UTF-8 sequence).

Muss ich beim Speichern noch irgendwas explizit maskieren oder so? Für das XML benutze ich dom4j.


----------



## Murray (9. Feb 2009)

Wie serialisierst Du das Objekt denn? Das Problem wird wohl darin bestehen, dass die Serialisierung Binärdaten erzeugt, während eine XML-Datei eine Textdatei ist. Üblicherweise würde man die die Binärdaten in eine passende Textrepräsentation verpacken. Dazu kann man z.B. das Base-64-Encoding verwenden.


----------



## peez (10. Feb 2009)

Zum (De-)Serialisieren habe ich mir zwei Klassen geschrieben, die OutputStream erweitern u. einfach in einen String schreiben bzw. davon lesen.

Mit dem Base64 Encoding funktioniert irgendwas nicht.
Wenn ich testweise einfach im Programm hin- und herencode funktioniert alles.

Speichere ich den Base64 String allerdings im XML und lade diesen später wieder, bekomme ich beim Deserialisieren (also in.readObject()) eine UTFDataFormatException...

Habe keine Idee was ich da noch beachten muss...


----------



## Murray (10. Feb 2009)

Zeig doch mal etwas Code.


----------



## Ebenius (10. Feb 2009)

Wie sieht denn das XML aus, wenn Du Base64 in den Knoten speicherst?


----------



## peez (10. Feb 2009)

Ok. Also so speichere ich:


```
Hashtable<String, Serializable> hash = new Hashtable<String, Serializable>();
hash.put( "key", "value1" );
		
OutputStream2Text ott = new OutputStream2Text(  ); //Meine Klasse
ObjectOutputStream o;
o = new ObjectOutputStream(ott);			
o.writeObject( hash );
String serialized = ott.getText();
String encoded = new BASE64Encoder(  ).encodeBuffer( serialized.getBytes() );			

Element e = new Element("data").setText(encoded);
```

Und so wird dann gelesen:


```
String encoded = e.getText(); //Text aus XML Element
decoded = new String(new BASE64Decoder(  ).decodeBuffer( encoded ));
ObjectInputStream i = new ObjectInputStream( new Text2InputStream(decoded) );
Hashtable<String,Serializable> newHash = (Hashtable<String,Serializable>)i.readObject();
```



Die beiden Stream Klassen sehen so aus:

```
private class Text2InputStream extends InputStream {

		private byte m_bytes[] = null;

		private int m_position = 0;

		public Text2InputStream( String text ) {
					m_bytes = text.getBytes();
				
		}

		@Override
		public int read() throws IOException {

			if ( m_position == m_bytes.length ) { // Am Ende eine -1 zurückgeben
				return -1;
			}

			return (int)m_bytes[m_position++];
		}

	}

	private class OutputStream2Text extends OutputStream {

		private Vector<Byte> m_bytes;
		

		public OutputStream2Text() {
			m_bytes = new Vector<Byte>();
		}

		@Override
		public void write( int b ) throws IOException {
			m_bytes.add( new Byte( (byte)b ) );
		}

		public String getText() {
			byte b[] = new byte[m_bytes.size()];

			for ( int i = 0; i < m_bytes.size(); i++ ) {
				b[i] = m_bytes.get( i ).byteValue();
			}

		
					return new String( b);
			

		}

	}
```

Im XML steht dann folgendes:

<additionalData>rO0ABXNyABNqYXZhLnV0aWwuSGFzaHRhYmxlE7sPJSFK5LgDAAJGAApsb2FkRmFjdG9ySQAJdGhy ZXNob2xkeHA/QAAAAAAACHcIAAAACwAAAAB4</additionalData>

(original rauskopiert)


----------



## Murray (10. Feb 2009)

Hier passen XML und Code nicht zusammen: im Code steht _data_ im XML aber _additional Data_. Das liegt aber vermutlich an irgendwelchen Vereinfachungen beim Posten und dürfte daher für das Problem wohl nicht so wichtig sein.

Drei Fragen aber:
1. Was sind das für Base64Encoder / Decoder?
2. Warum verwendest Du nicht ByteArrayOutputStream und ByteArrayInputStream zur Serialisierung?
3. Läuft Dein Test innerhalb einer VM, so dass unterschiedliches Platform-Default-Encoding definitiv ausgeschlossen werden kann?


----------



## Ebenius (10. Feb 2009)

Was soll denn das mit den String-Operationen auf den Byte-Arrays? So geht's richtig: 
	
	
	
	





```
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Eine Klasse die funktioniert
ObjectOutputStream o; 
o = new ObjectOutputStream(ott);          
o.writeObject( hash ); 
String serialized = ott.getText(); 
String encoded = new BASE64Encoder().encodeBuffer(baos.toByteArray());
```

Und so geht's zurück: 
	
	
	
	





```
final String encoded = e.getText(); //Text aus XML Element 
final byte[] decoded = new BASE64Decoder().decodeBuffer(encoded);
final ObjectInputStream i = new ObjectInputStream(new ByteArrayInputStream(decoded));
```
Code im Browser getippt, also nicht getestet.

Ebenius


----------



## peez (17. Feb 2009)

Hmm.. Funktioniert auch so nicht.
Kann es evt. an dom4j liegen? Wenn ich den selben Weg nämlich innerhalb des Programms nur über Stringvariablen gehe, funktioniert alles.
Wird der Base64 String im XML gespeichert, kommt beim Parsen des XMLs der Fehler "Invalid byte 2 of 2-byte UTF-8 sequence".
Könnte es Sinn machen, mal einen anderen XML parser zu benutzen?


----------



## Ebenius (17. Feb 2009)

Wie schreibst und liest Du denn das XML? Dort würde ich den Fehler vermuten.

Ebenius


----------



## peez (17. Feb 2009)

Eigentlich auf ganz übliche weise:

Lesen:

```
SAXReader reader = new SAXReader();
Document doc = reader.read( src );
```

Schreiben:

```
FileWriter w = new FileWriter( dst );
doc.write( w );
```


----------



## Ebenius (17. Feb 2009)

Hmm... Kannst Du mal das XML insgesamt posten? Oder zumindest soweit, dass die charset-Angabe (erste Zeile) drin ist und der Teil der serialisierten Instanz der schief geht?


----------



## peez (18. Feb 2009)

Das sieht so aus:

```
<?xml version="1.0" encoding="UTF-8"?>

<DPSubtitleSet>
  <Project>
    <name>(CREPÚSCULO)subt.inglés</name>
    <additionalData>rO0ABXNyABNqYXZhLnV0aWwuSGFzaHRhYmxlE7sPJSFK5LgDAAJGAApsb2FkRmFjdG9ySQAJdGhy ZXNob2xkeHA/QAAAAAAACHcIAAAACwAAAAB4</additionalData>
    <fps>25.0</fps>
```


----------



## Murray (18. Feb 2009)

Tritt der Fehler jetzt beim Parsen des XMLs auf? Oder beim Deserialisieren des Objekts aus dem Text-Content des additionalData-Elements heraus?

Ist ersteres der Fall, dann kann es auch an den anderen Sonderzeichen liegen.

Komisch am Base64-encodeten String ist das Leerzeichen - die sollten da eigentlich nicht vorkommen.


----------



## peez (18. Feb 2009)

Der Fehler tritt beim Parsen auf.
Wegen dem Leerzeichen - ich benutze diesen Encoder von Sun, der mit Java mitkommt. Der sollte doch eigentlich funktionieren, oder?


----------



## Spacerat (18. Feb 2009)

Ganz heisser Tip: Schau dir mal den Quelltext von
	
	
	
	





```
(String).getBytes()
```
in der API an. Dann siehst du so in etwa, was unter Umständen mit deinen Binärdaten beim Serialisieren passiert und warum diese dann nicht wieder deserialisiert werden können. Deie Klasse würde ich wie oben schon vorgeschlagen in einen ByteArrayOutputStream schreiben und anschliessend mit Base64-Encoding in die XML-Datei.


----------



## peez (20. Feb 2009)

Hmm.. Und was kann ich da sehen?


----------



## Spacerat (23. Feb 2009)

... das da "StringCoding.encoding()" verwendet wird. Das bedeutet, das der String encodiert wird, was bei Binärstrings (z.B. Serialisierte Daten) recht fatal sein kann / ist, sobald auch nur ein Byte über 0x7F (127) fälschlicherweise als Unicode interpretiert wird. Binärstrings packt man am besten mit einer eigenen Schleife in ein Array, oder man leitet einen InputStream in einen ByteArrayOutputStream um.

```
String tmp = "someBinaryData";
byte[] buffer = new byte[tmp.length()];
for(int n = 0; n < buffer.length; n++) buffer[n] = (byte) (tmp.charAt(n) & 0xFF);
// oder
InputStream in = new FileInputStream("someBinaryData");
ByteArrayOutputStream out = new ByteArrayOutputStream();
int n;
while((n = in.read()) != -1) out.write(n);
in.close();
out.close();
byte[] buffer = out.toByteArray();
```


----------



## peez (24. Feb 2009)

Habe das Problem gefunden. Das Encoding war immer richtig aber dom4j scheint ein Problem mit UTF-8 speichern zu haben...
Das selbe passiert nämlich auch, wenn innerhalb der XML Texte mit Unicode Zeichen vorkommen.
Habe jetzt auf JAXP umgestellt und schon funktionierts. Muss mich nur noch dran gewöhnen, dass es die Methode .element(String name) nicht mehr gibt ;-)

Ach und weiß jemand, wie man mit diesem Transformer ein "Pretty Print" hinbekommt?


----------



## Ebenius (24. Feb 2009)

peez hat gesagt.:


> Ach und weiß jemand, wie man mit diesem Transformer ein "Pretty Print" hinbekommt?


Hängt von der Implementation ab. Auf jeden Fall das: [HIGHLIGHT="Java"]transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$[/HIGHLIGHT]
... und ich mach zusätzlich noch das: [HIGHLIGHT="Java"]transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); //$NON-NLS-1$[/HIGHLIGHT]

Ebenius


----------



## peez (25. Feb 2009)

Super. Jetzt bin ich wunschlos glücklich ;-)

Vielen Dank für eure Hilfe!


----------



## Wildcard (25. Feb 2009)

peez hat gesagt.:


> ich benutze diesen Encoder von Sun, der mit Java mitkommt. Der sollte doch eigentlich funktionieren, oder?


Nein. Das knallt dir auf jeder anderen VM auf die Bretter, weil du sun* packages nicht verwenden darfst.


----------



## peez (26. Feb 2009)

Wildcard hat gesagt.:


> Nein. Das knallt dir auf jeder anderen VM auf die Bretter, weil du sun* packages nicht verwenden darfst.



Wie nicht verwenden? Lizenztechnisch oder weil es die Packages nicht gibt?
Was gibts denn für andere VMs? Die kommen doch alle von Sun oder?


----------



## Ebenius (26. Feb 2009)

peez hat gesagt.:


> Was gibts denn für andere VMs? Die kommen doch alle von Sun oder?


Nein. Dazu findet sich schnell das: IBM Runtime Environment for Java 2 (JRE), der GNU Interpreter for Java, SableVM, Kaffe.

Und sicher gibt's noch mehr.

Verwenden kannst Du alles was Du hier findest: Java™ Platform, Standard Edition 6 - API Specification.

Ebenius


----------



## Wildcard (26. Feb 2009)

http://java.sun.com/products/jdk/faq/faq-sun-packages.html


----------



## Wildcard (26. Feb 2009)

Ebenius hat gesagt.:


> Und sicher gibt's noch mehr.


Auch das sind wohl kaum alle:
http://en.wikipedia.org/wiki/List_of_Java_virtual_machines


----------

