# BLOB mit XML übertragbar?



## Ollek (26. Jul 2011)

Hallo,

ist es möglich, ein BLOB aus einer Datenbank in eine XML zu schreiben?
Oder gibt es dort einen Weg, wie es funktioniert? Habe schon einiges ausprobiert, nix geholfen. 

Viele Grüße


----------



## nillehammer (26. Jul 2011)

Ja gibt es. Für die Übertragung binärer Daten in XML gibt es zwei Datentypen base64Binary und hexBinary.

So gehst Du vor:
- Du nimmst Deinen Blob und wandelst ihn in ein byte-Array um
- Du encodierst das byte-Array z.B. mit commons codec (Base64 (Commons Codec 1.5 API))
- Du schreibst das ergebnis nach xml

Gruß nillehammer


----------



## AlexSpritze (26. Jul 2011)

Ich hätte noch folgende Alternative anzubieten, die ohne zusätzliche Bibliotheken auskommt. Du baust dir eine einfache Wrapper-Klasse für das Byte-Array, das der Bean-Konvention folgt. Objekte davon kannst du einfach mittels java.bean.XMLDecoder und XMLEncoder in XML schreiben und davon auch wieder lesen. Folgender Code ist ungetestet, sollte aber das Schema deutlich machen:


```
public class BlobToXMLTest{
  public static class BlobXMLWrapper{
    private byte[] blob;
    public BlobXMLWrapper(){ }
    public void setBlob(byte[] blob) { this.blob=blob; }
    public byte[] getBlob() { return blob; }
  }
  
  public static void main(String[] args) throws Exception{
    
    byte[] BYTE_ARRAY = null; // hier dein byte array, das du aus der DB hast
    BlobXMLWrapper blobXMLWrapper = new BlobXMLWrapper();
    blobXMLWrapper.setBlob(BYTE_ARRAY);
    
    XMLEncoder enc = new XMLEncoder(new FileOutputStream(new File("DATEINAME")));
    enc.writeObject(blobXMLWrapper);
    enc.flush();
    
    XMLDecoder dec = new XMLDecoder(new FileInputStream(new File("DATEINAME")));
    BlobXMLWrapper readObject = (BlobXMLWrapper) dec.readObject();
    byte[] arra = readObject.getBlob(); // Byte-Array ist wieder deserialisiert.
  }
}
```

PS: Die XMLDecoder und XMLEncoder sollten auch nach Gebrauch geschlossen werden.


----------



## nillehammer (26. Jul 2011)

Hallo Alex,
Dein Vorschlag ist schön einfach. Danke für den Tipp. Werde es bestimmt mal benutzen. Allerdings ist die Struktur des erzeugten XMLs ja festgelegt. Deswegen ist XMLEncoder für selbst definierte XML-Strukturen nicht geeignet. Oder habe ich was übersehen?
Gruß nillehammer


----------



## AlexSpritze (26. Jul 2011)

nillehammer hat gesagt.:


> Allerdings ist die Struktur des erzeugten XMLs ja festgelegt. Deswegen ist XMLEncoder für selbst definierte XML-Strukturen nicht geeignet. Oder habe ich was übersehen?



Ich denke, die Struktur des XML kann man hier nicht weiter beeinflussen. Diese XML*coder sind soweit ich das sehe, eine Alternative zur binären Serialisierung (Stichwort Interface Serializable), damit man z.B. die Objekte in der XML-Datei bearbeiten kann. Liegt ja alles in Klartext darin. Aber was meinst du mit selbstdefinierten XML-Strukturen?
Gruß


----------



## nillehammer (26. Jul 2011)

> Diese XML*coder sind soweit ich das sehe, eine Alternative zur binären Serialisierung


Ja, so hab ich das auch verstanden. "Mal schnell und einfach in lesbarer Form den Zustand eines Objekts wegschreiben." gefällt mir



> Aber was meinst du mit selbstdefinierten XML-Strukturen?


Alles, was man mit XML-Schemas definiert.


----------



## AlexSpritze (26. Jul 2011)

nillehammer hat gesagt.:


> Ja, so hab ich das auch verstanden. "Mal schnell und einfach in lesbarer Form den Zustand eines Objekts wegschreiben." gefällt mir
> Alles, was man mit XML-Schemas definiert.



Achso, dann ist es nichts für die selbstdefinierten Schemata. Auch ist vom Serialisieren des byte-Arrays hiermit abzuraten, weil die XML bestimmt locker die zehnfache Größe als das Array haben wird ...


----------



## Wildcard (26. Jul 2011)

Du kannst ein Schema erstellen und dir daraus von EMF Java Klassen generieren lassen.
EMF übernimmt dann das schreiben der XML und das kodieren nach Base64.


----------



## Ollek (27. Jul 2011)

[XML]
<maincat>
    <id>1</id>
    <app_id>1</app_id>
    <lfdnr>1</lfdnr>
    <description>Tisch</description>
    <color>WHITE</color>
    <subcats>
      <subcats>
        <id>1</id>
        <maincat_id>1</maincat_id>
        <lfdnr>1</lfdnr>
        <description>Tragetaschen</description>
        <articles>
          <article>
            <id>1</id>
            <subcat_id>1</subcat_id>
            <artnr>0000</artnr>
            <lfdnr>1</lfdnr>
            <description>Tragetasche Maxi</description>
            <img></img>
          </article>
        </articles>
       </subcats>
</maincat>
[/XML]

So sieht meine XML aus. In Das Tag  <img></img> muss nun das Image rein.
Bin noch sehr neu auf dem Pfad XML/Java. 
Wildcard, deswegen meine Frage an dich, was meinst du da genau?

Was einer weitere Schwierigkeit darstellen wird, wie baue ich die BLOB Konventierung in die Struktur ein?? Ich arbeite mit JDOM..


----------



## RySa (27. Jul 2011)

Hast du jetzt Probleme mit der Konvertierung, oder damit, das konvertierte in die Datei rein zu schreiben ?

Außerdem finde ich die Struktur der XML-Datei hmm... sagen wir mal "nicht so schön". Erstens, hast du glaube ich ein Tippfehler - <subcats> kommt zweimal hintereinander ohne dass, das erste geschlossen wird. Dann hast du noch das root-element <maincat> und irgendwo unter subcats ein element <maincat_id> ... warum nicht einfach so was <maincat id=1>, Elemente können auch Attribute haben, aber das nur so nebenbei.


----------



## Ollek (27. Jul 2011)

Bin nun soweit, dass ich die BLOB Datei in ein Byte[] umgewandelt habe. Nun muss ich den Schritt machen, die Daten in die Datei zu schreiben, sprich so zu konventieren, dass ich es in die Datei schreiben kann..


----------



## RySa (27. Jul 2011)

Zum encoden, wie schon vorher beschrieben 

```
Base64.encodeBase64(byte[])
```

Was das schreiben angeht, du nimmst entweder JDOM oder SAX/StAX und passt das Programm an die Struktur deiner XML-Datei an und schreibst das kodierte byte[] an die gewünschte Stelle rein. Oder du könntest gleichzeitig mir ein Gefallen tun, und mein Tool ausprobieren, das XML-Dateien bearbeiten kann  ---> XMLTool

Das wäre dann ungefähr so ein Aufwand:

```
public static void main(String args[]) {
		XMLTool tool;
		try {
			tool = new XMLTool();
			tool.setLoggerLevel(Level.ALL);
			tool.setFormatting(true);
			tool.setFile(new File("src/tests/xml-samples/bloob.xml"));
			XPath xpath = new XPath("maincats/maincat/subcats/subcat/articles/article[aID=1]/img/");
                        byte[] deinBloob = null; //Hier natürlich deine Daten
                        String daten = Base64.encodeBase64String(deinBloob)
			tool.addTextToElement(xpath, daten);
			tool.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
```


----------



## Ollek (27. Jul 2011)

Werde ich gleich mal ausprobieren.
Aber du sagtest, die Struktur meiner XML Datei passt noch nicht wirklich bzw. ist nicht gut.
Soll ich ID als Attribute speichern??

Bzw. hättest du nen Vorschlag für eine bessere Struktur, ich bin recht neu was XML angeht.. Erstes Programm mit XML 

Die XML Datei soll Hauptkategorien deren Unterkategorien und dessen Artikel speichern. Dort woltle ich nicht 3 XML machen sondern die Unterkategorien und Artikel direkt unter die Hauptkategorien packen.


----------



## RySa (27. Jul 2011)

Also erstens ist dein root-Element nicht wirklich ein Root-Element, es sei dem du hast nur ein Abschnitt gepostet. Zweitens, kann ich es nicht wirklich "verbessern" ohne die ganze Struktur zu kennen, und ohne zu wissen wofür genau und wie es eingesetzt werden sollte. Falls das aber die ganze Datei ist, solltest noch ein "echtes" root-element hinzufügen wie z.b <maincats> (ich nehme mal an, es soll mehr <maincat> Elemente in einer Datei geben.)

Was heißt denn, app_id und was soll die lfdnr heißen in der XML ? Ist denn lfdnr (also Laufende Nummer, nehme ich an) nicht überflüssig wenn es die id gibt ? Und was hat eine Tragetasche in der mainKategorie Tisch verloren ? xD Auch das Color, würde ich nicht unbedingt an die Mainkategorie dran hängen, da dann alle subcats und artikeln weiß sein müssten (also müsstest du für jede Farbe des Tisches, eine maincat haben, nicht wirklich effizient)

Vielleicht so als Prototyp, so etwas ?:
[XML]<?xml version="1.0" encoding="UTF-8" ?>
<maincats>
	<maincat mID="1">
    	<app_id>1</app_id>
    	<lfdnr>1</lfdnr>
    	<description>Tisch</description>
    	<color>WHITE</color>
    	<subcats>
      		<subcat sID="1">
       		 <lfdnr>1</lfdnr>
       		 <description>Tragetaschen</description>
       		 <articles>
         		 <article aID="1" aNR="0000">
            		<lfdnr>1</lfdnr>
            		<description>Tragetasche Maxi</description>
            		<img></img>
          		</article>
        	</articles>
      		</subcat>
     	</subcats>
	</maincat>
</maincats>[/XML]


----------



## Ollek (27. Jul 2011)

Habe es nun mit folgender Klasse gelöst:

Der Vorteil ist, dass ich keine externe JAR einbinden muss :toll:


```
public class Base64Coding
{
  private final static char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
  private static int[] toInt = new int[128];
 
  static
  {
    for (int i = 0; i < ALPHABET.length; i++)
    {
      toInt[ALPHABET[i]] = i;
    }
  }
 
  /**
   * Codiert das übergeben Byte[] in einen Base64-String
   *    *
   * @param Byte[] welches codiert werden soll
   * @return Der Base64-Codierte String
   */
  public static String encode(byte[] buf)
  {
	  
    int size = buf.length;
    char[] ar = new char[((size + 2) / 3) * 4];
    int a = 0;
    int i = 0;
    while (i < size)
    {
      byte b0 = buf[i++];
      byte b1 = (i < size) ? buf[i++] : 0;
      byte b2 = (i < size) ? buf[i++] : 0;
 
      int mask = 0x3F;
      ar[a++] = ALPHABET[(b0 >> 2) & mask];
      ar[a++] = ALPHABET[((b0 << 4) | ((b1 & 0xFF) >> 4)) & mask];
      ar[a++] = ALPHABET[((b1 << 2) | ((b2 & 0xFF) >> 6)) & mask];
      ar[a++] = ALPHABET[b2 & mask];
    }
    switch (size % 3)
    {
      case 1:
        ar[--a] = '=';
      case 2:
        ar[--a] = '=';
    }
    return new String(ar);
  }
 
  /**
   * Decodiert einen Base64-Codierte String in einen "normalen" String
   *
   * @param base64 Der Base64-Codierte String.
   * @return Der Base64-decodierte String.
   */
  public static String decode(String base64)
  {
    int delta = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
    byte[] buffer = new byte[base64.length() * 3 / 4 - delta];
    int mask = 0xFF;
    int index = 0;
    for (int i = 0; i < base64.length(); i += 4)
    {
      int c0 = toInt[base64.charAt(i)];
      int c1 = toInt[base64.charAt(i + 1)];
      buffer[index++] = (byte) (((c0 << 2) | (c1 >> 4)) & mask);
      if (index >= buffer.length)
      {
        return new String(buffer);
      }
      int c2 = toInt[base64.charAt(i + 2)];
      buffer[index++] = (byte) (((c1 << 4) | (c2 >> 2)) & mask);
      if (index >= buffer.length)
      {
        return new String(buffer);
      }
      int c3 = toInt[base64.charAt(i + 3)];
      buffer[index++] = (byte) (((c2 << 6) | c3) & mask);
    }
    return new String(buffer);
  }
}
```


----------

