Serialize a Bitmap in C#/.Net to Xml

Serialize a Bitmap in C#/.NET to XML

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);
}
}
}
}

Bitmap XML Serialization

You could make a property that get/sets your bitmap as a byte array. This should be base-64 encoded by the serializer.

public byte[] MyImageBytes {
get {
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(MyImage, typeof(byte[]));
}
}

You would probably also want to hide your Bitmap property with an [XmlIgnore] attribute. You may also want to prefer LinqToXml to the serializer as it gives you far more control.

Note that XmlSerializer performs quite badly when base-64 encoding. This is also the case for LinqToXml, the BitConverter class does a fine job converting to base-64 though.

Serialize and Store an Image in an XML File

XmlSerializer can't serialize or deserialize the WPF image types like BitmapImage etc. It is however able to (de)serialize byte arrays. So you may add a byte[] ImageBuffer property to your Person class, which contains the binary image data. You would then also set the XmlIgnore attribute on the Image property to suppress its (de)serialization, and set XmlElement("Image") on the ImageBuffer properties to (de)serialize it as <Image>...</Image>.

public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }

[XmlIgnore]
public BitmapSource Image { get; set; }

[XmlElement("Image")]
public byte[] ImageBuffer
{
get
{
byte[] imageBuffer = null;

if (Image != null)
{
using (var stream = new MemoryStream())
{
var encoder = new PngBitmapEncoder(); // or some other encoder
encoder.Frames.Add(BitmapFrame.Create(Image));
encoder.Save(stream);
imageBuffer = stream.ToArray();
}
}

return imageBuffer;
}
set
{
if (value == null)
{
Image = null;
}
else
{
using (var stream = new MemoryStream(value))
{
var decoder = BitmapDecoder.Create(stream,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
Image = decoder.Frames[0];
}
}
}
}
}

This approach has also been suggested for properties of type Bitmap in this answer.

Serialize a System.Windows.Media.ImageSource object

You can't serialize an image to XML, but you can save it to a MemoryStream and encode the binary data to base64.

string ImageToBase64(BitmapSource bitmap)
{
var encoder = new PngBitmapEncoder();
var frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using(var stream = new MemoryStream())
{
encoder.Save(stream);
return Convert.ToBase64String(stream.ToArray());
}
}

BitmapSource Base64ToImage(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
using(var stream = new MemoryStream(bytes))
{
return BitmapFrame.Create(stream);
}
}

Note that base64 is not very efficient in terms of space... If possible, it would be better to transmit the image in binary form, rather than in XML.

C#: Why would an object that implements Serializable wouldn't be possible to serialize?

There's a hint in the stacktrace:

InnerException: System.InvalidOperationException                    
Message="The type System.Drawing.Bitmap was not expected.
Use the XmlInclude or SoapInclude attribute to specify types
that are not known statically."

System.Drawing.Bitmapis not Xml Serializable, hence the exception. Perhaps another way would be to use XmlIgnoreAttribute to ignore this property, or implement IXmlSerializableand serialize the image as an ascii blob if you really need it saved?

See the related question & answer Serializing a Bitmap in C# to XML

contributed by Conrad Frix in comments

Saving ImageSource (BitmapSource) in XML

There's a problem with Base64ToImage method from this answer. The documentation states that with the default OnDemand cache option the stream must not be closed before the image is actually used. In your case this means that the Image element is trying to access the already disposed stream.

The fix is pretty simple, you just need to change the cache option to OnLoad and the problem is gone:

BitmapSource Base64ToImage(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
using (var stream = new MemoryStream(bytes))
{
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}


Related Topics



Leave a reply



Submit