views:

976

answers:

4

I want to XML-Serialize a complex type (class), that has a property of type System.Drawing.Bitmap among others.

    /// <summary>
    /// Gets or sets the large icon, a 32x32 pixel image representing this face.
    /// </summary>
    /// <value>The large icon.</value>
    public Bitmap LargeIcon { get; set; }

I now have found out that serializing the Bitmap with the default XML serializer does not work, because it does not have a public parameterless constructor, which is mandatory with the default xml serializer.

I am aware of the following:

I rather would not like referencing another project nor extensively tweak my class to just allow xml serialization of those bitmaps.

Is there no way to keep that simple?

Many thanks, Marcel

+1  A: 

The BitMap class has not been designed to be easily XML Serialized. So, no, there's not simple way to correct a design decision.

John Saunders
Seems you are right, an that is the cause of my troubles.
Marcel
+1 for pointing this out
Marcel
+2  A: 

You can also to implement ISerializable and to use SerializationInfo to deal manually with your bitmap content.

EDIT: João is right: Correct way to deal with XML serialization is to implement IXmlSerializable, not ISerializable:

public class MyImage : IXmlSerializable
{
    public string Name  { get; set; }
    public Bitmap Image { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        throw new NotImplementedException();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteStartElement("Name");
        writer.WriteString(this.Name);
        writer.WriteEndElement();

        using(MemoryStream ms = new MemoryStream())
        {
            this.Image.Save(ms, ImageFormat.Bmp );
            byte[] bitmapData = ms.ToArray();
            writer.WriteStartElement("Image");
            writer.WriteBase64(bitmapData, 0, bitmapData.Length);
            writer.WriteEndElement();
        }
    }
}
Rubens Farias
+8  A: 

I would do something like:

[XmlIgnore]
public Bitmap LargeIcon { get; set; }

[Browsable(false),EditorBrowsable(EditorBrowsableState.Never)]
[XmlElement("LargeIcon")]
public byte[] LargeIconSerialized
{
    get { // serialize
        if (LargeIcon == null) return null;
        using (MemoryStream ms = new MemoryStream()) {
            LargeIcon.Save(ms, ImageFormat.Bmp);
            return ms.ToArray();
        }
    }
    set { // deserialize
        if (value == null) {
            LargeIcon = null;
        } else {
            using (MemoryStream ms = new MemoryStream(value)) {
                LargeIcon = new Bitmap(ms);
            }
        }
    }
}
Marc Gravell
This is similar to the solution provided by the link in the original post. However to me, this still seems to be the most simple solution.
Marcel
Frankly, it is the only sensible option with `XmlSerializer`. Implementing `IXmlSerializable` is asking for pain.
Marc Gravell
+1 for this solution. The source code works well for me. Many Thanks.
Marcel
I have a little trouble using this code. Would the Byte array be automatically filled upon deserializing? And if yes, would I have to convert that array into a Bitmap afterwards? I currently get an "InvalidOperationException" when the deserializing gets to the line with the bmp. Thanks for the help!
Dinoo
@Dinoo - it should just work via the properties. Which .NET version are you using? (out of curiosity)
Marc Gravell
@Marc I am using 3.5. Would that make such a difference?
Dinoo
@Dinoo - no; given the date, that is most likely what I used when I wrote this answer... does the exception say anything else?
Marc Gravell
@Marc - Nope, it just states the exception and at which line in the xml file it occured. I guess I will post a question after I beat my head against the wall a little more.
Dinoo
An interesting thing that happened is I commented-out the "Get" part of the Byte[] and went without trouble even though I am only deserializing. However, the bitmaps are still null and a break point in the "Set" part never actually breaks. I am quite clueless now.
Dinoo
@Dinoo - if you remove the get *completely*, it will ignore the property. `return null;` would be a reasonable (if confusing) `get`
Marc Gravell
For posterity I am successfully using this method, except that instead of `LargIcon.Save` in `LargeIconSerialized.get` I have to use the `TypeDescriptor.GetConverter` method as used in this example: http://www.codeproject.com/KB/XML/xml_serializationasp.aspx
DGGenuine
+1  A: 

Implement IXmlSerializable and then handle all the serialization details yourself.

Since you say it's a large type and you only have a problem with the bitmap consider doing something like this:

public class BitmapContainer : IXmlSerializable
{
    public BitmapContainer() { }

    public Bitmap Data { get; set; }

    public XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(XmlReader reader)
    {
        throw new NotImplementedException();
    }

    public void WriteXml(XmlWriter writer)
    {
        throw new NotImplementedException();
    }
}

public class TypeWithBitmap
{
    public BitmapContainer MyImage { get; set; }

    public string Name { get; set; }
}
João Angelo
Not very practicable. This would involve a derived class and quite much code.
Marcel