【问题标题】:Serializing private member data序列化私有成员数据
【发布时间】:2010-10-22 14:37:14
【问题描述】:

我正在尝试将一个对象序列化为具有许多属性的 XML,其中一些属性是只读的。

public Guid Id { get; private set; }

我已经标记了类 [Serializable] 并且我已经实现了 ISerializable 接口。

下面是我用来序列化对象的代码。

public void SaveMyObject(MyObject obj)
{
    XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
    TextWriter tw = new StreamWriter(_location);
    serializer.Serialize(tw, obj);
    tw.Close();
}

不幸的是,它在这条消息的第一行发生了变化。

InvalidOperationException 未处理: 无法生成临时类(结果=1)。 错误 CS0200:无法将属性或索引器“MyObject.Id”分配给 -- 它是只读的

如果我将 Id 属性设置为 public,它可以正常工作。谁能告诉我我是否正在做某事,或者至少是否有可能?

【问题讨论】:

    标签: c# .net xml serialization serializable


    【解决方案1】:

    您可以使用DataContractSerializer(但请注意,您不能使用 xml 属性 - 只能使用 xml 元素):

    using System;
    using System.Runtime.Serialization;
    using System.Xml;
    [DataContract]
    class MyObject {
        public MyObject(Guid id) { this.id = id; }
        [DataMember(Name="Id")]
        private Guid id;
        public Guid Id { get {return id;}}
    }
    static class Program {
        static void Main() {
            var ser = new DataContractSerializer(typeof(MyObject));
            var obj = new MyObject(Guid.NewGuid());
            using(XmlWriter xw = XmlWriter.Create(Console.Out)) {
                ser.WriteObject(xw, obj);
            }
        }
    }
    

    或者,您可以实现 IXmlSerializable 并自己做所有事情 - 但这至少适用于 XmlSerializer

    【讨论】:

    • 我已将代码更改为使用 DataContractSerializer,并且我注意到它仍在运行 GetObjectData 方法。我是否认为我可以将属性放在我的属性上以对其进行序列化,或者我可以实现 ISerializable 接口?
    • 如果你实现 ISerializable(或者是 IXmlSeializable?),你基本上是在自己做所有的工作......
    • 这对我有用,但后来我发现使用 DataMemberAttribute 序列化私有成员仅在完全信任环境中运行时才有效,而不是在部分信任环境中。一个解决方案是使成员内部而不是私有。详情见blog.walteralmeida.com/2010/05/…
    【解决方案2】:

    您可以使用System.Runtime.Serialization.NetDataContractSerializer。它更强大,修复了经典 Xml 序列化器的一些问题。

    请注意,这个有不同的属性。

    [DataContract]
    public class X
    {
      [DataMember]
      public Guid Id { get; private set; }
    }
    
    
    NetDataContractSerializer serializer = new NetDataContractSerializer();
    TextWriter tw = new StreamWriter(_location);
    serializer.Serialize(tw, obj);
    

    编辑:

    根据 Marc 的评论更新:您可能应该使用 System.Runtime.Serialization.DataContractSerializer 来获取干净的 XML。其余代码相同。

    【讨论】:

    • NetDataContractSerializer 不写入 xml... - 或者更确切地说,它不是适合外部使用的干净 xml - 它包含程序集元数据。
    • @Marc:感谢您的提示。它总是取决于一个人想要实现什么。 DataContractSerializer 可能是这里所期望的。
    【解决方案3】:

    只读字段不会使用XmlSerializer 序列化,这是由于readonly 关键字的性质

    来自 MSDN:

    readonly 关键字是可以在字段上使用的修饰符。当字段声明包含 readonly 修饰符时,对该声明引入的字段的赋值只能作为声明的一部分或在同一类的构造函数中发生。

    所以...您几乎需要在默认构造函数中设置字段值...

    【讨论】:

    • 我认为因为我已经实现了 ISerializable.GetObjectData 方法,所以 XmlSerializer 会使用它来获取我想要序列化的信息,而不是尝试访问我的只读属性。
    • XmlSerializer 不关心 ISerializable - 只关心 IXmlSerializable
    【解决方案4】:

    使用特定的序列化模式是不可能的(有关解决方法,请参阅其他 cmets)。如果你真的想保持你的序列化模式不变,你必须解决这个框架的限制。看到这个example

    本质上,标记属性public,但如果在反序列化以外的任何时间访问它,则抛出异常。

    【讨论】:

    • “但抛出异常”——由于 XmlSerializer 不支持序列化回调,所以你无从得知...
    • 您可以使用System.Diagnostics.StackTrace 来了解您的财产是什么,但我不推荐这样的解决方案:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 1970-01-01
    • 2020-09-04
    • 2013-08-31
    • 1970-01-01
    • 2011-06-18
    相关资源
    最近更新 更多