序列化私有成员数据

76

我正在尝试将一个具有多个属性的对象序列化为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,它就可以正常工作。有人能告诉我我是否做错了什么,或者它是否可能吗?

4个回答

65

你可以使用 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接口? - Jon Mitchell
如果你实现了ISerializable(或者是IXmlSeializable?),那么基本上所有的工作都要由你自己完成... - Marc Gravell
4
这对我有用,但后来我发现只有在完全信任的环境下,使用DataMemberAttribute序列化私有成员才能起作用,而在部分信任的环境下则不行。解决办法是将成员改为internal而非private。详情请参见http://blog.walteralmeida.com/2010/05/wcf-and-datacontract-serialization-internals-and-tips-.html。 - Peladao

6
你可以使用 System.Runtime.Serialization.NetDataContractSerializer。它比传统的 Xml Serializer 更强大,并解决了一些问题。
请注意,这个序列化器有不同的属性。
[DataContract]
public class X
{
  [DataMember]
  public Guid Id { get; private set; }
}


NetDataContractSerializer serializer = new NetDataContractSerializer();
TextWriter tw = new StreamWriter(_location);
serializer.Serialize(tw, obj);

编辑:

根据马克的评论更新: 对于你的情况,应该使用 System.Runtime.Serialization.DataContractSerializer 以获得干净的XML。 其余的代码保持不变。


NetDataContractSerializer不会写入XML,或者说,它不是适合外部使用的干净的XML格式 - 它包含程序集元数据。 - Marc Gravell
@Marc:感谢你的提示。这总是取决于一个人想要实现什么。DataContractSerializer可能是这里所期望的。 - Stefan Steinegger

2

只读字段不会使用XmlSerializer进行序列化,这是由于readonly关键字的特性所致。

来自MSDN:

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

所以...您基本上需要在默认构造函数中设置字段的值...


我曾认为由于我已经实现了ISerializable.GetObjectData方法,XmlSerializer会使用它来获取我想要序列化的信息,而不是尝试访问我的只读属性。 - Jon Mitchell
XmlSerializer 不关心 ISerializable 接口,只关心 IXmlSerializable 接口。 - Marc Gravell

0

使用该特定序列化模式是不可能的(请参见其他评论以获取解决方法)。如果您真的想保留您的序列化模式,您必须在此问题上绕过框架限制。请参阅此示例

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


6
“但是抛出一个异常” - 由于XmlSerializer不支持序列化回调,你无法知道... - Marc Gravell
1
你可以使用 System.Diagnostics.StackTrace 来查找调用你的属性的方法,但我不建议这样做 :-) - Louis Somers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接