自定义序列化

时间:2017-03-14 19:18:49

标签: c# serialization deserialization

我有一些必须序列化的对象:

class Displayable{
  string name;
  Sprite icon;
}

icon字段需要自定义序列化,因为Sprite已经被序列化(在不同的文件中,具有自己的格式,由游戏引擎)并且我只需要存储引用它们的方法(假设stringDictionary<string, Sprite>内的关键字。

使用BinaryFormatter我尝试实现ISerializableISerializationSurrogate,但这两种方法都会在反序列化时创建Sprite对象的新实例,因此它们不适合我的情况。我希望具有ISerializationSurrogate的相同功能,除了我不想要SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)中的第一个参数,因为我需要返回一个我已经在内存中的对象而不是从反序列化器接收一个新实例并用数据填充。

我也尝试过像SharpSerializer这样的外部库,但是我不喜欢使用公共无参数构造函数的限制,它并不能让你自定义特殊类的序列化。 我已经阅读了ProtoBuffers,但它不支持继承,我需要它在其他地方。

所以我的要求是:

  • 继承支持
  • 能够为某些类型定义自定义序列化
  • 这些自定义类型的反序列化不应该在它自己的
  • 上创建实例

有没有图书馆这样做?

否则,我是否过于挑剔?你通常如何实现对存储在其他地方的对象的引用的序列化?

提前谢谢。

编辑: 这就是我想要的

public class SerializableSprite : ISerializationSurrogate
{
  public static Dictionary<string, Sprite> sprites;
  public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) {
    Sprite sprite = obj as Sprite;
    info.AddValue("spriteKey", sprite.name);
  }
  // The first parameter in this function is a newly instantiated Sprite, which I don't need
  public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
    return sprites[info.GetString("spriteKey")];
  }
}

1 个答案:

答案 0 :(得分:4)

为了防止BinaryFormatter在反序列化期间创建Sprite的新实例,在序列化期间,您可以调用SerializationInfo.SetType(Type)来指定备用类型信息 - 通常是一些代理类型 - 插入序列化流。在反序列化期间,SetObjectData()将传递代理的实例,而不是&#34; real&#34;要初始化的类型。此代理类型必须依次实现IObjectReference,以便&#34;真实&#34;最终可以将对象插入到对象图中,特别是通过在精灵表中查找它。

以下是:

class ObjectReferenceProxy<T> : IObjectReference
{
    public T RealObject { get; set; }

    #region IObjectReference Members

    object IObjectReference.GetRealObject(StreamingContext context)
    {
        return RealObject;
    }

    #endregion
}

public sealed class SpriteSurrogate : ObjectLookupSurrogate<string, Sprite>
{
    static Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>();
    static Dictionary<Sprite, string> spriteNames = new Dictionary<Sprite, string>();

    public static void AddSprite(string name, Sprite sprite)
    {
        if (name == null || sprite == null)
            throw new ArgumentNullException();
        sprites.Add(name, sprite);
        spriteNames.Add(sprite, name);
    }

    public static IEnumerable<Sprite> Sprites
    {
        get
        {
            return sprites.Values;
        }
    }

    protected override string GetId(Sprite realObject)
    {
        if (realObject == null)
            return null;
        return spriteNames[realObject];
    }

    protected override Sprite GetRealObject(string id)
    {
        if (id == null)
            return null;
        return sprites[id];
    }
}

public abstract class ObjectLookupSurrogate<TId, TRealObject> : ISerializationSurrogate where TRealObject : class
{
    public void Register(SurrogateSelector selector)
    {
        foreach (var type in Types)
            selector.AddSurrogate(type, new StreamingContext(StreamingContextStates.All), this);
    }

    IEnumerable<Type> Types
    {
        get
        {
            yield return typeof(TRealObject);
            yield return typeof(ObjectReferenceProxy<TRealObject>);
        }
    }

    protected abstract TId GetId(TRealObject realObject);

    protected abstract TRealObject GetRealObject(TId id);

    #region ISerializationSurrogate Members

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var original = (TRealObject)obj;
        var id = GetId(original);
        info.AddValue("id", id);
        // use Info.SetType() to force the serializer to construct an object of type ObjectReferenceWrapper<TRealObject> during deserialization.
        info.SetType(typeof(ObjectReferenceProxy<TRealObject>));
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        // Having constructed an object of type ObjectReferenceWrapper<TRealObject>, 
        // look up the real sprite using the id in the serialization stream.
        var wrapper = (ObjectReferenceProxy<TRealObject>)obj;
        var id = (TId)info.GetValue("id", typeof(TId));
        wrapper.RealObject = GetRealObject(id);
        return wrapper;
    }

    #endregion
}

然后将其应用于BinaryFormatter,如下所示:

        var selector = new SurrogateSelector();
        var spriteSurrogate = new SpriteSurrogate();
        spriteSurrogate.Register(selector);

        BinaryFormatter binaryFormatter = new BinaryFormatter(selector, new StreamingContext());

示例fiddle

<强>更新

虽然上面的代码适用于.Net 3.5及更高版本,但由于IObjectReference存在一些问题,尽管在那里成功编译,但它显然无法在中运行。以下内容也适用于.Net 3.5及更高版本,并通过从ISerializationSurrogate.SetObjectData()返回实际对象来避免使用IObjectReference。因此它也应该在unity3d中工作(在评论中确认):

public sealed class SpriteSurrogate : ObjectLookupSurrogate<string, Sprite>
{
    static Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>();
    static Dictionary<Sprite, string> spriteNames = new Dictionary<Sprite, string>();

    public static void AddSprite(string name, Sprite sprite)
    {
        if (name == null || sprite == null)
            throw new ArgumentNullException();
        sprites.Add(name, sprite);
        spriteNames.Add(sprite, name);
    }

    public static IEnumerable<Sprite> Sprites
    {
        get
        {
            return sprites.Values;
        }
    }

    protected override string GetId(Sprite realObject)
    {
        if (realObject == null)
            return null;
        return spriteNames[realObject];
    }

    protected override Sprite GetRealObject(string id)
    {
        if (id == null)
            return null;
        return sprites[id];
    }
}

public abstract class ObjectLookupSurrogate<TId, TRealObject> : ISerializationSurrogate where TRealObject : class
{
    class SurrogatePlaceholder
    {
    }

    public void Register(SurrogateSelector selector)
    {
        foreach (var type in Types)
            selector.AddSurrogate(type, new StreamingContext(StreamingContextStates.All), this);
    }

    IEnumerable<Type> Types
    {
        get
        {
            yield return typeof(TRealObject);
            yield return typeof(SurrogatePlaceholder);
        }
    }

    protected abstract TId GetId(TRealObject realObject);

    protected abstract TRealObject GetRealObject(TId id);

    #region ISerializationSurrogate Members

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        var original = (TRealObject)obj;
        var id = GetId(original);
        info.AddValue("id", id);
        // use Info.SetType() to force the serializer to construct an object of type SurrogatePlaceholder during deserialization.
        info.SetType(typeof(SurrogatePlaceholder));
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        // Having constructed an object of type SurrogatePlaceholder, 
        // look up the real sprite using the id in the serialization stream.
        var id = (TId)info.GetValue("id", typeof(TId));
        return GetRealObject(id);
    }

    #endregion
}

示例fiddle #2