XML-Serialisierung von Images / Bitmaps

    • C#

    Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

      XML-Serialisierung von Images / Bitmaps

      Heute wollen wir uns mit dem Speichern und Laden von Bildern in einer XML-Datei befassen.
      Letztenendes ist das nicht sehr kompliziert, es müssen nur die richtigen Komponenten zusammengesetzt werden.
      Ein Bild (hier im Bitmap-Format) wird per MemoryStream in ein Byte-Array geschrieben,
      das Byte-Array wird in einen formatierten Base64-String konvertiert,
      dieser String wird in der XML-Datei abgelegt:
      Get

      C#-Quellcode

      1. get
      2. {
      3. // Serialisierung:
      4. // Bereichstest
      5. if (this.Image == null)
      6. {
      7. return null;
      8. }
      9. // Byte-Array, in dem das Image gespeichert wird
      10. byte[] array;
      11. // das Image in ein Byte-Array speichern
      12. using (MemoryStream ms = new MemoryStream())
      13. {
      14. this.Image.Save(ms, ImageFormat.Png);
      15. array = ms.ToArray();
      16. }
      17. // Länge aufgerundet bestimmen:
      18. // https://de.wikipedia.org/wiki/Base64
      19. int len = (int)(array.Length * 1.4);
      20. // Char-Array für den Base64-String
      21. char[] charArray = new char[len];
      22. // das Char-Array in einen formatierten Base64-String konvertieren
      23. int len2 = Convert.ToBase64CharArray(array, 0, array.Length, charArray, 0, Base64FormattingOptions.InsertLineBreaks);
      24. // den Base64-String mit der korrekten Länge zurückgeben
      25. return new String(charArray, 0, len2);
      26. }

      Die Gegenrichtung ist äquivalent:
      Der aus der XNL-Datei gelesene Base64-String wird in ein Byte-Array konvertiert,
      mit diesem wird ein MemoryStream instanziiert,
      mit diesem wird die Ziel-Bitmap erstellt:
      Set

      C#-Quellcode

      1. set
      2. {
      3. // Deserialisierung:
      4. // Aus dem Base64-String ein Byte-Array generieren
      5. byte[] array = Convert.FromBase64CharArray(value.ToCharArray(), 0, value.Length);
      6. // und als Bitmap instanziieren
      7. using (MemoryStream ms = new MemoryStream(array))
      8. {
      9. this.Image = new Bitmap(ms);
      10. }
      11. }

      Der ganze Code wird in eine Datenklasse gepackt, und es wird dafür gesorgt, dass nicht die Bitmap, sondern der Base64-String in die XML-Datei geschrieben wird:
      Image

      C#-Quellcode

      1. /// <summary>
      2. /// Das zu serialisierende Image,
      3. /// das nicht serialisiert wird
      4. /// </summary>
      5. [XmlIgnore]
      6. public Bitmap Image { get; set; }
      7. /// <summary>
      8. /// Der aus Image ermittelte formatierte Base64-String,
      9. /// der unter dem Namen "Image" serialisiert wird
      10. /// </summary>
      11. [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
      12. [XmlElement("Image")]
      13. public string ImageSerialized
      14. {
      15. {
      16. get { ... }
      17. set { ... }
      18. }
      19. }

      Das Lesen und Schreiben der XML-Datei selbst erfolgt mit diesem Code:
      Speichern und Laden

      C#-Quellcode

      1. /// <summary>
      2. /// XML-Serialisierung der Instanz einer Datenklasse
      3. /// </summary>
      4. /// <param name="data">zu serialisierende Instanz</param>
      5. /// <param name="file">Pfad zum Abspeichern</param>
      6. public void WriteData(MyData data, string file)
      7. {
      8. // Serialize object to a XML file.
      9. using (StreamWriter sw = new StreamWriter(file, false, Encoding.Default))
      10. {
      11. XmlSerializer x = new XmlSerializer(data.GetType());
      12. x.Serialize(sw, data);
      13. }
      14. }
      15. /// <summary>
      16. /// XML-Deserialisierung einer Datenklasse
      17. /// </summary>
      18. /// <param name="file">Pfad zum Einlesen</param>
      19. /// <returns>
      20. /// die erstellte Instanz der Datenklasse
      21. /// </returns>
      22. public MyData LoadData(string file)
      23. {
      24. MyData data = new MyData();
      25. try
      26. {
      27. // Deserialize XML file to a new object.
      28. using (StreamReader sr = new StreamReader(file, Encoding.Default))
      29. {
      30. XmlSerializer x = new XmlSerializer(data.GetType());
      31. data = (MyData)x.Deserialize(sr);
      32. }
      33. return data;
      34. }
      35. catch
      36. {
      37. // nix tun, die Daten-Instanz ist nicht valid,
      38. // es wird die leere Instanz zurückgegeben
      39. }
      40. return data;
      41. }

      Im Anhang das komplette Projekt:
      XmlSerializedImage.zip
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!
      Um das speichern/laden etwas generischer zu gestalten:
      Spoiler anzeigen

      C#-Quellcode

      1. /// <summary>
      2. /// XML-Serialisierung der Instanz einer Datenklasse
      3. /// </summary>
      4. /// <param name="data">zu serialisierende Instanz</param>
      5. /// <param name="file">Pfad zum Abspeichern</param>
      6. public void WriteData<T>(T data, string file)
      7. {
      8. // Serialize object to a XML file.
      9. using (StreamWriter sw = new StreamWriter(file, false, Encoding.Default))
      10. {
      11. XmlSerializer x = new XmlSerializer(typeof(T));
      12. x.Serialize(sw, data);
      13. }
      14. }
      15. /// <summary>
      16. /// XML-Deserialisierung einer Datenklasse
      17. /// </summary>
      18. /// <param name="file">Pfad zum Einlesen</param>
      19. /// <returns>
      20. /// die erstellte Instanz der Datenklasse
      21. /// </returns>
      22. public T LoadData<T>(string file)
      23. {
      24. T data = default(T);
      25. try
      26. {
      27. // Deserialize XML file to a new object.
      28. using (StreamReader sr = new StreamReader(file, Encoding.Default))
      29. {
      30. XmlSerializer x = new XmlSerializer(typeof(T));
      31. data = (T)x.Deserialize(sr);
      32. }
      33. return data;
      34. }
      35. catch
      36. {
      37. // nix tun, die Daten-Instanz ist nicht valid,
      38. // es wird die leere Instanz zurückgegeben
      39. }
      40. return data;
      41. }
      Meiner Meinung nach macht es nur wenig Sinn, ein Image zu XML zu serialisieren. Ein Bild ist meistens relativ groß von den Daten her und wird noch viel größer, wenn es zu einem String konvertiert wird. Bei der Deserialisierung werden alle Daten der XML Datei in den Arbeitsspeicher geladen, was zu einer unnötig hohen Arbeitsspeicher Auslastung führt. Außerdem müssen bei jedem Speichern alle Daten wieder neu geschrieben werden, was durch die sehr großen Datenmengen sehr langsam ist. Dafür bieten sich Datenbanken eher an, für XML würde ich empfehlen die Bilder in einem Ordner zu speichern und statt dem Base64 String einfach nur den Dateinamen zu speichern.

      Für ein paar kleinere Bilder mag das ok sein, aber das macht meiner Meinung nach keinen Sinn größere Mengen.
      Mfg
      Vincent

      Ich dachte, mit dem XML-Serializer werden Bilder automatisch in einen String umgewandelt.

      Bessert mich aus, wenn das nicht der Fall sein sollte
      Auf der Suche nach Coding-Kursen auf Lence.at
      ja, aber die Deserialisierung scheitert (habs probiert). Eine Bitmap kann nicht als Teil aus einem Stream ausgelesen werden, sondern verlangt ihren "eigenen" Stream, wo die BitmapDaten an Position 0 beginnen.
      Ist unerfreulich, und verletzt das Liskovsche Architektur-Prinizip ("keine Überraschungen!"), aber vermutlich eine Optimierung im Zusammenhang mit Kompressions-Verfahren.

      Aber das eigene Hantieren mit Base64Stream kann wohl entfallen, denn ein XmlSerialisierer verwendet bereits von sich aus diese Codierung, wenn er ein Byte-Array persistiert.
      Aber die Konvertierung in ein Byte-Array ist unumgänglich.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Ich habe noch etwas entdeckt, was die Serealisierung vereinfachen könnte:

      VB.NET-Quellcode

      1. System.Drawing.Imageconverter


      Mit dieser Klasse kann man anscheinend mit

      VB.NET-Quellcode

      1. Imageconverter.ConvertToString
      und

      VB.NET-Quellcode

      1. Imageconverter.ConvertFromString
      Bilder als Text ausgeben lassen und somit als String serialisieren

      Grüße
      Auf der Suche nach Coding-Kursen auf Lence.at
      @KingTimon Ich hab mir das mal angesehen und auch (nicht funktionierende) Beispiele gefunden:


      -----
      Dies hier funktiontert, allerdings nicht in einen String:

      C#-Quellcode

      1. public static byte[] ImageToByte(Image img)
      2. {
      3. ImageConverter converter = new ImageConverter();
      4. return (byte[])converter.ConvertTo(img, typeof(byte[]));
      5. }
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      Programmierfragen über PN / Konversation werden ignoriert!