是否可以在 Silverlight 中使用 protobuf-net 来(反)序列化私有财产?

发布于 2024-11-08 05:19:01 字数 2009 浏览 0 评论 0原文

众所周知,Silverlight 不允许私有反射。尽管如此,我还是有一个带有私有设置器的公共财产,我需要能够对其进行序列化(这里没有问题)和反序列化(令人沮丧)。

我知道世界上没有任何东西可以使 protobuf-net 写入 Silverlight 中的此属性,这必须在客户端类型(或程序集,如果属性是内部的)内完成。

Silverlight 的 protobuf-net 中是否有一个使之成为可能的方案?我可以使该类型实现一些专门的 protobuf-net 接口(例如 IProtoSerialized)。

谢谢。

编辑

我可以提出这样的方案:

[ProtoMember(N, SetterMethod = "DeserializePropValue")]
public string property Prop
{
  get { return m_prop; }
  private set { m_prop = value; }
}

public void DeserializePropValue(ProtoValue<string> value)
{
  m_prop = value.Value;
}

其中类型 ProtoValue 是公共的,但其构造函数是内部的,因此只有 protobuf-net 程序集可以创建该类型的实例。当然,protobuf-net 不会公开任何公共 API 来创建 ProtoValue 对象。

该方案仅支持 Silverlight 平台,其他平台只会调用私有 setter。

你怎么认为?

EDIT2

我想指出的是,人们肯定仍然可以获得对任意 PropValue的引用。例如,但这不会是偶然的,这些是我希望消除的属性的意外覆盖。另外,我想让 setter 保持非公开状态,这样它就不会出现在 UI 中使用的各种基于反射的绑定机制中。

EDIT3

PropValue实例可能不适合存储,这意味着在 DeserializePropValue 方法返回后,相应的 PropValue 实例将失效。这就只剩下一种滥用它的方式,就像这样:

[ProtoContract]
public class Abusee
{
    [ProtoMember(1, SetterMethod = "DeserializePropValue")]
    public string property Prop { get; private set; }

    public void DeserializePropValue(ProtoValue<string> value)
    {
      m_prop = value.Value;
    }
}

[ProtoContract]
public class Abuser
{
  private Abusee m_abusee;

  public Abuser(Abusee abusee, string newPropValue)
  {
    m_abusee = abusee;
    Dummy = newPropValue;
    Serializer.DeepClone(this);
  }

  [ProtoMember(1, SetterMethod = "DeserializeDummyValue")]
  public string property Dummy
  {
    get;
    private set;
  }

  public void DeserializeDummyValue(ProtoValue<string> value)
  {
    m_abusee.DeserializePropValue(value);
  }
}

相当多的努力是偶然发生的。至于故意虐待——这里没有回归。人们总是可以序列化一个对象,操作二进制序列化数据,然后将其反序列化回来。回归仅在于滥用的容易程度。然而,我的目标是:

  • 防止意外错误
  • 保持 setter 不公开
  • 避免与代理相关的维护噩梦。

As we know Silverlight does not allow private reflection. Still, I have a public property with a private setter, which I need to be able to serialize (no problem here) and deserialize (bummer).

I know that nothing in the world would make protobuf-net write to this property in Silverlight, this must be done from within the client type (or assembly, if the property is made internal).

Is there a scheme in place in protobuf-net for Silverlight, which makes it possible? I could make the type implement some specialized protobuf-net interface (like IProtoSerializable, for instance).

Thanks.

EDIT

I can propose a scheme like that:

[ProtoMember(N, SetterMethod = "DeserializePropValue")]
public string property Prop
{
  get { return m_prop; }
  private set { m_prop = value; }
}

public void DeserializePropValue(ProtoValue<string> value)
{
  m_prop = value.Value;
}

Where the type ProtoValue is public, but its constructors are internal, so that only protobuf-net assembly can create instances of that type. And of course, protobuf-net will not expose any public API to create ProtoValue objects.

This scheme can be supported for Silverlight platform only, other platforms will just invoke the private setter.

What do you think?

EDIT2

I wish to note that surely one can still obtain a reference to an arbitrary PropValue<T> instance, but this will not be accidentally and these are the accidental overwrites of the property, that I wish to eliminate. Plus, I want to keep the setter non public, so that it does not surface in various reflection based binding mechanisms used in UI.

EDIT3

The PropValue<T> instances can be made unsuitable for storage, meaning after the method DeserializePropValue returns, the respective PropValue instance is invalidated. This leaves only one way to abuse it, like so:

[ProtoContract]
public class Abusee
{
    [ProtoMember(1, SetterMethod = "DeserializePropValue")]
    public string property Prop { get; private set; }

    public void DeserializePropValue(ProtoValue<string> value)
    {
      m_prop = value.Value;
    }
}

[ProtoContract]
public class Abuser
{
  private Abusee m_abusee;

  public Abuser(Abusee abusee, string newPropValue)
  {
    m_abusee = abusee;
    Dummy = newPropValue;
    Serializer.DeepClone(this);
  }

  [ProtoMember(1, SetterMethod = "DeserializeDummyValue")]
  public string property Dummy
  {
    get;
    private set;
  }

  public void DeserializeDummyValue(ProtoValue<string> value)
  {
    m_abusee.DeserializePropValue(value);
  }
}

Quite a lot of effort to happen by accident. As to intentional abuse - there is no regression here. One can always serialize an object, manipulate the binary serialization data and then deserialize it back. The regression is only in the ease of abuse. However, my goal is to:

  • Prevent mistakes by accident
  • Keep the setter non public
  • Avoid the maintenance nightmare associated with surrogates.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

若能看破又如何 2024-11-15 05:19:01

有趣的问题。 v2 中有一个“代理”的概念,它是为不可变对象(和结构)设计的,这可能有用 - 这取决于对象的复杂程度以及选项的吸引力。第二种选择可能是使属性表现出冰棒不变性。我将说明两者,但是:

  • 在这种情况下,冰棒不变性只是半冰棒,并且很容易被回避;这个选项很方便,如果你只是想防止意外损坏,可能会很有用,
  • 真正的不变性为你提供了更强的保护;本质上,代理充当“构建者”,但这永远不允许您改变现有实例。

请注意,目前无法在属性上指定代理(我可能会稍后添加;p) - 所以我使用了运行时模型来演示这一点:

class Program
{
    static void Main()
    {
        var obj = new Popsicle(3, 4);
        var clone = Serializer.DeepClone(obj);
        Debug.Assert(clone.Foo == obj.Foo);
        Debug.Assert(clone.Bar == obj.Bar);

        var model = TypeModel.Create();
        model.Add(typeof(MutableSurrogate), false).Add("Foo", "Bar");
        model.Add(typeof(ImmutableType), false).SetSurrogate(typeof(MutableSurrogate));
        // note you should re-use models (cache them) - or better: pre-generate a serializer dll
        var obj2 = new ImmutableType(5, 6);
        var clone2 = (ImmutableType)model.DeepClone(obj2);
        Debug.Assert(clone2.Foo == obj2.Foo);
        Debug.Assert(clone2.Bar == obj2.Bar);
    }
}

[ProtoContract] // should also work with DataContract etc
public class Popsicle
{
    public Popsicle() { }
    public Popsicle(int foo, int bar)
    {
        Foo = foo;
        this.bar = bar;
    }
    private int isBeingDeserialized;

    [ProtoBeforeDeserialization]
    public void BeforeDeserialization()
    {
        isBeingDeserialized++;
    }
    [ProtoAfterDeserialization]
    public void AfterDeserialization()
    {
        isBeingDeserialized--;
    }
    [ProtoMember(1)]
    public int Foo { get; set; } //  fully mutable

    private int bar;
    [ProtoMember(2)]
    public int Bar
    {
        get { return bar; }
        set
        {
            if (bar == value) return;
            if (isBeingDeserialized <= 0) throw new InvalidOperationException();
            bar = value;
        }
    }
}

public class ImmutableType
{
    private readonly int foo, bar;
    public ImmutableType(int foo, int bar)
    {
        this.foo = foo;
        this.bar = bar;
    }
    public int Foo { get { return foo; } }
    public int Bar { get { return bar; } }
}
public class MutableSurrogate
{
    public static implicit operator ImmutableType(MutableSurrogate surrogate)
    {            
        return surrogate == null ? null
            : new ImmutableType(surrogate.Foo, surrogate.Bar);
    }
    public static implicit operator MutableSurrogate(ImmutableType surrogate)
    {
        return surrogate == null ? null
            : new MutableSurrogate { Foo = surrogate.Foo, Bar = surrogate.Bar };
    }
    public int Foo { get; set; }
    public int Bar { get; set; }
}

我目前没有类似 IProtoSerialized 的东西。我仍在等待找到真正需要它的一件事......我不确定就是这个。

Interesting question. There is a concept of "surrogates" in v2, which was designed for immutable objects (and structs), which might be of use - it depends how complex the object is as to how attractive an option that is. A second option might be to make the property exhibit popsicle immutability. I will illustrate both, but:

  • popsicle immutability in this case is only semi-popsicle, and could easily be side-stepped; this option is convenient, and may be useful if you just want to prevent accidental damage
  • true immutability gives you stronger protection; essentially the surrogate acts as a "builder", but that never allows you to mutate an existing instance

Note that surrogates can't be specified on an attribute at the moment (I might add that later ;p) - so I've used the runtime model to demonstrate this:

class Program
{
    static void Main()
    {
        var obj = new Popsicle(3, 4);
        var clone = Serializer.DeepClone(obj);
        Debug.Assert(clone.Foo == obj.Foo);
        Debug.Assert(clone.Bar == obj.Bar);

        var model = TypeModel.Create();
        model.Add(typeof(MutableSurrogate), false).Add("Foo", "Bar");
        model.Add(typeof(ImmutableType), false).SetSurrogate(typeof(MutableSurrogate));
        // note you should re-use models (cache them) - or better: pre-generate a serializer dll
        var obj2 = new ImmutableType(5, 6);
        var clone2 = (ImmutableType)model.DeepClone(obj2);
        Debug.Assert(clone2.Foo == obj2.Foo);
        Debug.Assert(clone2.Bar == obj2.Bar);
    }
}

[ProtoContract] // should also work with DataContract etc
public class Popsicle
{
    public Popsicle() { }
    public Popsicle(int foo, int bar)
    {
        Foo = foo;
        this.bar = bar;
    }
    private int isBeingDeserialized;

    [ProtoBeforeDeserialization]
    public void BeforeDeserialization()
    {
        isBeingDeserialized++;
    }
    [ProtoAfterDeserialization]
    public void AfterDeserialization()
    {
        isBeingDeserialized--;
    }
    [ProtoMember(1)]
    public int Foo { get; set; } //  fully mutable

    private int bar;
    [ProtoMember(2)]
    public int Bar
    {
        get { return bar; }
        set
        {
            if (bar == value) return;
            if (isBeingDeserialized <= 0) throw new InvalidOperationException();
            bar = value;
        }
    }
}

public class ImmutableType
{
    private readonly int foo, bar;
    public ImmutableType(int foo, int bar)
    {
        this.foo = foo;
        this.bar = bar;
    }
    public int Foo { get { return foo; } }
    public int Bar { get { return bar; } }
}
public class MutableSurrogate
{
    public static implicit operator ImmutableType(MutableSurrogate surrogate)
    {            
        return surrogate == null ? null
            : new ImmutableType(surrogate.Foo, surrogate.Bar);
    }
    public static implicit operator MutableSurrogate(ImmutableType surrogate)
    {
        return surrogate == null ? null
            : new MutableSurrogate { Foo = surrogate.Foo, Bar = surrogate.Bar };
    }
    public int Foo { get; set; }
    public int Bar { get; set; }
}

I don't currently have anything like an IProtoSerializable. I'm still waiting to find that one thing that truly needs it... I'm not sure this is it.

旧城烟雨 2024-11-15 05:19:01

看起来 Jackson - Java Json 序列化器很好地解决了不可变类问题: http ://www.cowtowncoder.com/blog/archives/2010/08/entry_409.html

基本上注释工厂/构造函数方法并将其输入参数映射到不可变/只读属性。

Looks like Jackson - the Java Json serializer solved the immutable class problem quite nicely: http://www.cowtowncoder.com/blog/archives/2010/08/entry_409.html

Basically annotate on the factory/constructor method and map its input parameters to immutable/read only properties.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文