定制序列化 [英] Customized Serialization

查看:93
本文介绍了定制序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  class Displayable {
字符串名称;
Sprite图标;
}

icon 字段需要自定义序列化,因为 Sprite s已经被序列化(游戏引擎以不同的文件,具有自己的格式),我只需要存储一种引用它们的方法(让我们例如说字符串,它是 Dictionary< string,Sprite> )中的键。



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



<我还尝试了SharpSerializer之类的外部库,但是我不喜欢使用公共的无参数构造函数的约束,并且它实际上并不能让您自定义特殊类的序列化。
我已经了解了ProtoBuffers,但是它不支持继承,我需要在其他地方使用。



所以我的要求是:




  • 继承支持

  • 能够为某些类型定义自定义序列化

  • 反序列化这些自定义类型中的不应该自己创建实例



是否有任何库在这样做?



否则,我太挑剔了吗?通常如何实现对存储在其他位置的对象的引用的序列化?



请先谢谢您。



编辑:
这就是我想要的东西

 公共类SerializableSprite:ISerializationSurrogate 
{
公共静态Dictionary< string,Sprite>精灵
public void GetObjectData(object obj,SerializationInfo info,StreamingContext context){
Sprite sprite = obj as Sprite;
info.AddValue( spriteKey,sprite.name);
}
// //此函数中的第一个参数是新实例化的Sprite,我不需要
公共对象SetObjectData(object obj,SerializationInfo info,StreamingContext context,ISurrogateSelector选择器){
return sprites [info.GetString( spriteKey)];
}
}


解决方案

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



执行以下操作:

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

#region IObjectReference成员

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

#endregion
}

公共密封类SpriteSurrogate:ObjectLookupSurrogate< string,Sprite>
{
静态Dictionary< string,Sprite> sprites = new Dictionary< string,Sprite>();
静态Dictionary< Sprite,string> spriteNames = new Dictionary< Sprite,string>();

公共静态无效AddSprite(字符串名称,Sprite sprite)
{
if(name == null || sprite == null)
throw new ArgumentNullException() ;
sprites.Add(name,sprite);
spriteNames.Add(sprite,name);
}

公共静态IEnumerable< Sprite> Sprites
{
get
{
返回sprites.Values;
}
}

受保护的覆盖字符串GetId(Sprite realObject)
{
if(realObject == null)
返回null;
return spriteNames [realObject];
}

受保护的重写Sprite GetRealObject(string id)
{
if(id == null)
返回null;
return sprites [id];
}
}

公共抽象类ObjectLookupSurrogate< TId,TRealObject> :ISerializationSurrogate其中TRealObject:类
{
public void注册(SurrogateSelector选择器)
{
foreach(类型中的变量类型)
选择器。AddSurrogate(类型,新的StreamingContext (StreamingContextStates.All),此);
}

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

受保护的抽象TId GetId(TRealObject realObject);

受保护的抽象TRealObject GetRealObject(TId id);

#region ISerializationSurrogate成员

public void GetObjectData(object obj,SerializationInfo info,StreamingContext context)
{
var original =(TRealObject)obj;
var id = GetId(原始);
info.AddValue( id,id);
//使用Info.SetType()强制序列化程序构造ObjectReferenceWrapper< TRealObject>类型的对象。在反序列化期间。
info.SetType(typeof(ObjectReferenceProxy< TRealObject>)));
}

公共对象SetObjectData(对象obj,SerializationInfo信息,StreamingContext上下文,ISurrogateSelector选择器)
{
//构造了ObjectReferenceWrapper< TRealObject>类型的对象,
//使用序列化流中的ID查找真实的Sprite。
var wrapper =(ObjectReferenceProxy< TRealObject>)obj;
var id =(TId)info.GetValue( id,typeof(TId));
wrapper.RealObject = GetRealObject(id);
返回包装;
}

#endregion
}

然后将其应用于 BinaryFormatter 如下:

  var选择器=新的SurrogateSelector (); 
var spriteSurrogate = new SpriteSurrogate();
spriteSurrogate.Register(选择器);

BinaryFormatter binaryFormatter =新的BinaryFormatter(选择器,新的StreamingContext());

样本小提琴



更新



同时上面的代码在.Net 3.5及更高版本中有效,它显然在unity3d中不起作用尽管由于 IObjectReference 的某些问题而在此处成功编译。以下内容也可在.Net 3.5及更高版本中使用,并且通过从 IObjectReference /en-us/library/system.runtime.serialization.iserializationsurrogate.setobjectdata(v=vs.110).aspx rel = nofollow noreferrer> ISerializationSurrogate.SetObjectData() 。因此,它也应该在unity3d中工作(已在评论中确认):

 公共密封类SpriteSurrogate:ObjectLookupSurrogate< string,Sprite> 
{
static Dictionary< string,Sprite> sprites = new Dictionary< string,Sprite>();
静态Dictionary< Sprite,string> spriteNames = new Dictionary< Sprite,string>();

公共静态无效AddSprite(字符串名称,Sprite sprite)
{
if(name == null || sprite == null)
throw new ArgumentNullException() ;
sprites.Add(name,sprite);
spriteNames.Add(sprite,name);
}

公共静态IEnumerable< Sprite> Sprites
{
get
{
返回sprites.Values;
}
}

受保护的覆盖字符串GetId(Sprite realObject)
{
if(realObject == null)
返回null;
return spriteNames [realObject];
}

受保护的重写Sprite GetRealObject(string id)
{
if(id == null)
返回null;
return sprites [id];
}
}

公共抽象类ObjectLookupSurrogate< TId,TRealObject> :ISerializationSurrogate其中TRealObject:类
{
类SurrogatePlaceholder
{
}

public void Register(SurrogateSelector选择器)
{
foreach(类型为var类型)
选择器.AddSurrogate(类型,新的StreamingContext(StreamingContextStates.All),此);
}

IEnumerable< Type>类型
{
get
{
yield return typeof(TRealObject);
收益回报typeof(SurrogatePlaceholder);
}
}

受保护的抽象TId GetId(TRealObject realObject);

受保护的抽象TRealObject GetRealObject(TId id);

#region ISerializationSurrogate成员

public void GetObjectData(object obj,SerializationInfo info,StreamingContext context)
{
var original =(TRealObject)obj;
var id = GetId(原始);
info.AddValue( id,id);
//使用Info.SetType()强制序列化器在反序列化期间构造SurrogatePlaceholder类型的对象。
info.SetType(typeof(SurrogatePlaceholder));
}

公共对象SetObjectData(对象obj,SerializationInfo info,StreamingContext上下文,ISurrogateSelector选择器)
{
//构造了SurrogatePlaceholder类型的对象,
//使用序列化流中的id查找真实的sprite。
var id =(TId)info.GetValue( id,typeof(TId));
返回GetRealObject(id);
}

#endregion
}

样本小提琴#2


I have some objects that must be serialized:

class Displayable{
  string name;
  Sprite icon;
}

The icon field requires custom serialization since Sprites are already serialized (in different files, with their own format, by a game engine) and I only need to store a way to reference them (let's say a string, being the key inside a Dictionary<string, Sprite>).

Using BinaryFormatter I tried implementing ISerializable and ISerializationSurrogate but both of these methods create a new instance of the Sprite object on deserialization so they are not suitable for my case. I would like to have the same functionality of ISerializationSurrogate except I don't want the first parameter in SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) because I need to return an object I already have in memory instead of receiving a new instance from the deserializer and fill it with data.

I also tried external libraries like SharpSerializer but I don't like the constraints of having a public parameterless constructor and it doesn't really let you customize the serialization of special classes. I've read about ProtoBuffers but that doesn't support inheritance and I need it somewhere else.

So my requirements are:

  • Inheritance support
  • Being able to define custom serialization for some types
  • Deserialization of those custom types shouldn't create instances on it's own

Is there any library doing this?

Otherwise, am I being too picky? How do you usually achieve serialization of references to objects stored somewhere else?

Thank you in advance.

Edit: Here's what I'd like to have

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

解决方案

In order to prevent BinaryFormatter from creating a new instance of Sprite during deserialization, during serialization you can call SerializationInfo.SetType(Type) to specify alternate type information -- typically some proxy type -- to insert into the serialization stream. During deserialization SetObjectData() will be passed an instance of the proxy rather than the "real" type to initialize. This proxy type must in turn implement IObjectReference so that the "real" object can eventually be inserted into the object graph, specifically by looking it up in your table of sprites.

The following does this:

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
}

Then apply it to a BinaryFormatter as follows:

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

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

Sample fiddle.

Update

While the code above works in .Net 3.5 and above, it apparently does not work in despite compiling successfully there because of some problem with IObjectReference. The following also works in .Net 3.5 and above and also avoids the use of IObjectReference by returning the real object from ISerializationSurrogate.SetObjectData(). Thus it should work in unity3d as well (confirmed in comments):

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
}

Sample fiddle #2.

这篇关于定制序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆