如何序列化一个Class包含BitmapImage?
How to serialize a Class contains BitmapImage?
我有一个DeepCopy方法,它序列化传入参数的对象,returns返回反序列化的对象进行深拷贝。
我的方法是:
public static class GenericCopier<T>
{
public static T DeepCopy(object objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
如果传递给参数的对象不包含任何 BitmapImage 字段和属性,则效果很好。
public class MyClass
{
public string TestString {get; set;}
public BitmapImage TestImage { get; set;}
}
如果我对 MyClass 进行 DeepCopy,
MyClass orginal = new MyClass(){ TestString = "Test"};
MyClass copy = GenericCopier<MyClass>.DeepCopy(orginal);
它抛出异常
程序集中的 'System.Windows.Media.Imaging.BitmapImage' 类型未标记为可序列化
我找到了序列化BitmapImage的方法here
但是,我如何混合使用这两种类型的序列化(BinaryFormatter 和 PngBitmapEncoder)来序列化 MyClass?
这里有两个选择:
选项 1:实施 ISerializable
和快照到 PNG
你必须在这里做的是让包含你的 BitmapImage
的所有 类 实现 ISerializable
interface, then, in GetObjectData
, return a byte array representing an encoding of the image, for instance PNG. Then in the deserialization constructor 将 PNG 解码为新的 BitmapImage
。
请注意,这会对图像进行快照,因此可能会丢失一些 WPF 数据。
由于您可能有多个 类,其中包含一个 BitmapImage
,最简单的方法是引入一些包装器结构,其中包含从 BitmapImage
到 BitmapImage
的隐式转换,例如所以:
[Serializable]
public struct SerializableBitmapImageWrapper : ISerializable
{
readonly BitmapImage bitmapImage;
public static implicit operator BitmapImage(SerializableBitmapImageWrapper wrapper)
{
return wrapper.BitmapImage;
}
public static implicit operator SerializableBitmapImageWrapper(BitmapImage bitmapImage)
{
return new SerializableBitmapImageWrapper(bitmapImage);
}
public BitmapImage BitmapImage { get { return bitmapImage; } }
public SerializableBitmapImageWrapper(BitmapImage bitmapImage)
{
this.bitmapImage = bitmapImage;
}
public SerializableBitmapImageWrapper(SerializationInfo info, StreamingContext context)
{
byte[] imageBytes = (byte[])info.GetValue("image", typeof(byte[]));
if (imageBytes == null)
bitmapImage = null;
else
{
using (var ms = new MemoryStream(imageBytes))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = ms;
bitmap.EndInit();
bitmapImage = bitmap;
}
}
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
byte [] imageBytes;
if (bitmapImage == null)
imageBytes = null;
else
using (var ms = new MemoryStream())
{
BitmapImage.SaveToPng(ms);
imageBytes = ms.ToArray();
}
info.AddValue("image", imageBytes);
}
#endregion
}
public static class BitmapHelper
{
public static void SaveToPng(this BitmapSource bitmap, Stream stream)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(bitmap, stream, encoder);
}
public static void SaveUsingEncoder(this BitmapSource bitmap, Stream stream, BitmapEncoder encoder)
{
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
encoder.Save(stream);
}
public static BitmapImage FromUri(string path)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(path);
bitmap.EndInit();
return bitmap;
}
}
然后使用如下:
[Serializable]
public class MyClass
{
SerializableBitmapImageWrapper testImage;
public string TestString { get; set; }
public BitmapImage TestImage { get { return testImage; } set { testImage = value; } }
}
public static class GenericCopier
{
public static T DeepCopy<T>(T objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
选项 2:使用序列化代理直接克隆 BitmapImage
事实证明 BitmapImage
有一个 Clone()
方法,所以有理由问:是否有可能以某种方式覆盖二进制序列化以用克隆替换原始文件,而不实际序列化它?这样做可以避免快照为 PNG 的潜在数据丢失,因此看起来更可取。
事实上,使用包含代理创建的克隆副本 ID 的 serialization surrogates to replace the bitmap images with an IObjectReference
代理是可能的。
public static class GenericCopier
{
public static T DeepCopy<T>(T objectToCopy)
{
var selector = new SurrogateSelector();
var imageSurrogate = new BitmapImageCloneSurrogate();
imageSurrogate.Register(selector);
BinaryFormatter binaryFormatter = new BinaryFormatter(selector, new StreamingContext(StreamingContextStates.Clone));
using (MemoryStream memoryStream = new MemoryStream())
{
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
class CloneWrapper<T> : IObjectReference
{
public T Clone { get; set; }
#region IObjectReference Members
object IObjectReference.GetRealObject(StreamingContext context)
{
return Clone;
}
#endregion
}
public abstract class CloneSurrogate<T> : ISerializationSurrogate where T : class
{
readonly Dictionary<T, long> OriginalToId = new Dictionary<T, long>();
readonly Dictionary<long, T> IdToClone = new Dictionary<long, T>();
public void Register(SurrogateSelector selector)
{
foreach (var type in Types)
selector.AddSurrogate(type, new StreamingContext(StreamingContextStates.Clone), this);
}
IEnumerable<Type> Types
{
get
{
yield return typeof(T);
yield return typeof(CloneWrapper<T>);
}
}
protected abstract T Clone(T original);
#region ISerializationSurrogate Members
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
var original = (T)obj;
long cloneId;
if (original == null)
{
cloneId = -1;
}
else
{
if (!OriginalToId.TryGetValue(original, out cloneId))
{
Debug.Assert(OriginalToId.Count == IdToClone.Count);
cloneId = OriginalToId.Count;
OriginalToId[original] = cloneId;
IdToClone[cloneId] = Clone(original);
}
}
info.AddValue("cloneId", cloneId);
info.SetType(typeof(CloneWrapper<T>));
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
var wrapper = (CloneWrapper<T>)obj;
var cloneId = info.GetInt64("cloneId");
if (cloneId != -1)
wrapper.Clone = IdToClone[cloneId];
return wrapper;
}
#endregion
}
public sealed class BitmapImageCloneSurrogate : CloneSurrogate<BitmapImage>
{
protected override BitmapImage Clone(BitmapImage original)
{
return original == null ? null : original.Clone();
}
}
在此实现中,您的主要 类 保持不变:
[Serializable]
public class MyClass
{
BitmapImage testImage;
public string TestString { get; set; }
public BitmapImage TestImage { get { return testImage; } set { testImage = value; } }
}
尴尬的是,虽然 BitmapImage
有一个 Clone
方法,但它实际上并没有实现 ICloneable
接口。如果有,上面的代码看起来会更清晰,因为我们可以简单地克隆每个可克隆对象,而不是为 BitmapImage
.
调用特定方法
我有一个DeepCopy方法,它序列化传入参数的对象,returns返回反序列化的对象进行深拷贝。
我的方法是:
public static class GenericCopier<T>
{
public static T DeepCopy(object objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
如果传递给参数的对象不包含任何 BitmapImage 字段和属性,则效果很好。
public class MyClass
{
public string TestString {get; set;}
public BitmapImage TestImage { get; set;}
}
如果我对 MyClass 进行 DeepCopy,
MyClass orginal = new MyClass(){ TestString = "Test"};
MyClass copy = GenericCopier<MyClass>.DeepCopy(orginal);
它抛出异常
程序集中的 'System.Windows.Media.Imaging.BitmapImage' 类型未标记为可序列化
我找到了序列化BitmapImage的方法here
但是,我如何混合使用这两种类型的序列化(BinaryFormatter 和 PngBitmapEncoder)来序列化 MyClass?
这里有两个选择:
选项 1:实施 ISerializable
和快照到 PNG
你必须在这里做的是让包含你的 BitmapImage
的所有 类 实现 ISerializable
interface, then, in GetObjectData
, return a byte array representing an encoding of the image, for instance PNG. Then in the deserialization constructor 将 PNG 解码为新的 BitmapImage
。
请注意,这会对图像进行快照,因此可能会丢失一些 WPF 数据。
由于您可能有多个 类,其中包含一个 BitmapImage
,最简单的方法是引入一些包装器结构,其中包含从 BitmapImage
到 BitmapImage
的隐式转换,例如所以:
[Serializable]
public struct SerializableBitmapImageWrapper : ISerializable
{
readonly BitmapImage bitmapImage;
public static implicit operator BitmapImage(SerializableBitmapImageWrapper wrapper)
{
return wrapper.BitmapImage;
}
public static implicit operator SerializableBitmapImageWrapper(BitmapImage bitmapImage)
{
return new SerializableBitmapImageWrapper(bitmapImage);
}
public BitmapImage BitmapImage { get { return bitmapImage; } }
public SerializableBitmapImageWrapper(BitmapImage bitmapImage)
{
this.bitmapImage = bitmapImage;
}
public SerializableBitmapImageWrapper(SerializationInfo info, StreamingContext context)
{
byte[] imageBytes = (byte[])info.GetValue("image", typeof(byte[]));
if (imageBytes == null)
bitmapImage = null;
else
{
using (var ms = new MemoryStream(imageBytes))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = ms;
bitmap.EndInit();
bitmapImage = bitmap;
}
}
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
byte [] imageBytes;
if (bitmapImage == null)
imageBytes = null;
else
using (var ms = new MemoryStream())
{
BitmapImage.SaveToPng(ms);
imageBytes = ms.ToArray();
}
info.AddValue("image", imageBytes);
}
#endregion
}
public static class BitmapHelper
{
public static void SaveToPng(this BitmapSource bitmap, Stream stream)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(bitmap, stream, encoder);
}
public static void SaveUsingEncoder(this BitmapSource bitmap, Stream stream, BitmapEncoder encoder)
{
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
encoder.Save(stream);
}
public static BitmapImage FromUri(string path)
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(path);
bitmap.EndInit();
return bitmap;
}
}
然后使用如下:
[Serializable]
public class MyClass
{
SerializableBitmapImageWrapper testImage;
public string TestString { get; set; }
public BitmapImage TestImage { get { return testImage; } set { testImage = value; } }
}
public static class GenericCopier
{
public static T DeepCopy<T>(T objectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
选项 2:使用序列化代理直接克隆 BitmapImage
事实证明 BitmapImage
有一个 Clone()
方法,所以有理由问:是否有可能以某种方式覆盖二进制序列化以用克隆替换原始文件,而不实际序列化它?这样做可以避免快照为 PNG 的潜在数据丢失,因此看起来更可取。
事实上,使用包含代理创建的克隆副本 ID 的 serialization surrogates to replace the bitmap images with an IObjectReference
代理是可能的。
public static class GenericCopier
{
public static T DeepCopy<T>(T objectToCopy)
{
var selector = new SurrogateSelector();
var imageSurrogate = new BitmapImageCloneSurrogate();
imageSurrogate.Register(selector);
BinaryFormatter binaryFormatter = new BinaryFormatter(selector, new StreamingContext(StreamingContextStates.Clone));
using (MemoryStream memoryStream = new MemoryStream())
{
binaryFormatter.Serialize(memoryStream, objectToCopy);
memoryStream.Seek(0, SeekOrigin.Begin);
return (T)binaryFormatter.Deserialize(memoryStream);
}
}
}
class CloneWrapper<T> : IObjectReference
{
public T Clone { get; set; }
#region IObjectReference Members
object IObjectReference.GetRealObject(StreamingContext context)
{
return Clone;
}
#endregion
}
public abstract class CloneSurrogate<T> : ISerializationSurrogate where T : class
{
readonly Dictionary<T, long> OriginalToId = new Dictionary<T, long>();
readonly Dictionary<long, T> IdToClone = new Dictionary<long, T>();
public void Register(SurrogateSelector selector)
{
foreach (var type in Types)
selector.AddSurrogate(type, new StreamingContext(StreamingContextStates.Clone), this);
}
IEnumerable<Type> Types
{
get
{
yield return typeof(T);
yield return typeof(CloneWrapper<T>);
}
}
protected abstract T Clone(T original);
#region ISerializationSurrogate Members
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
var original = (T)obj;
long cloneId;
if (original == null)
{
cloneId = -1;
}
else
{
if (!OriginalToId.TryGetValue(original, out cloneId))
{
Debug.Assert(OriginalToId.Count == IdToClone.Count);
cloneId = OriginalToId.Count;
OriginalToId[original] = cloneId;
IdToClone[cloneId] = Clone(original);
}
}
info.AddValue("cloneId", cloneId);
info.SetType(typeof(CloneWrapper<T>));
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
var wrapper = (CloneWrapper<T>)obj;
var cloneId = info.GetInt64("cloneId");
if (cloneId != -1)
wrapper.Clone = IdToClone[cloneId];
return wrapper;
}
#endregion
}
public sealed class BitmapImageCloneSurrogate : CloneSurrogate<BitmapImage>
{
protected override BitmapImage Clone(BitmapImage original)
{
return original == null ? null : original.Clone();
}
}
在此实现中,您的主要 类 保持不变:
[Serializable]
public class MyClass
{
BitmapImage testImage;
public string TestString { get; set; }
public BitmapImage TestImage { get { return testImage; } set { testImage = value; } }
}
尴尬的是,虽然 BitmapImage
有一个 Clone
方法,但它实际上并没有实现 ICloneable
接口。如果有,上面的代码看起来会更清晰,因为我们可以简单地克隆每个可克隆对象,而不是为 BitmapImage
.