【问题标题】:Set Only On Deserialization仅在反序列化时设置
【发布时间】:2010-11-10 01:04:51
【问题描述】:

问题:

我有一个类,比如 Foo,它实现了一个 Id 属性。富必须是 可序列化的。 Foo.Id 应在初始化时初始化为新的 GUID 福。 Foo.Id 设置后不应更改。反序列化将尝试设置 Foo.Id,因此必须将其设为 Public。

Private _Id As String=system.Guid.NewGuid.tostring
Public Property Id As String
    Get
        return _Id
    End Get
    Set(ByVal value As String)
        _Id = value
    End Set
End Property

或者对于 c#ers

private string _Id = system.Guid.NewGuid().ToString();
public string Id {
    get { return _Id; }
    set { _Id = value; }
}

解决思路:

唯一的解决方案似乎是在设置 Foo.Id 时抛出运行时异常,但是 这将在反序列化期间引起问题。所以,我们必须以某种方式 确保仅在尝试 Set Foo.Id 时抛出异常 在序列化程序之外制作。构造函数中的某种标志或其他东西?

编辑、反序列化方法...

public static Foo DeserializeFromFile(string sFilespec)
{
    Xml.Serialization.XmlSerializer oSerializer = new Xml.Serialization.XmlSerializer(typeof(Foo));
    System.IO.FileStream oStream = new System.IO.FileStream(sFilespec, IO.FileMode.Open);
    Foo oObject = oSerializer.Deserialize(oStream);
    oStream.Close();
    return oObject;
}

【问题讨论】:

  • 您使用的是哪种序列化方式,什么版本的 .net?

标签: c# .net serialization constructor properties


【解决方案1】:

我不确定我是否理解你的问题,但你可以尝试在你的类中实现ISerializable 接口来手动微调序列化/反序列化过程。

[Serializable]
public class YourClass : ISerializable
{    
    private Guid _Id = Guid.NewGuid();

    public string Id
    {
            get { return _Id; }
            private set { _Id = value; }
    }

    public YourClass() // Normal constructor
    {
       // ...
    }

    // This constructor is for deserialization only
    private YourClass(SerializationInfo information, StreamingContext context)
    {
        Id = (Guid)information.GetValue("Id", typeof(Guid)); // etc
    }

    void ISerializable.GetObjectData(SerializationInfo information,
        StreamingContext context)
    {
        // You serialize stuff like this
        information.AddValue("Id", Id, typeof(Guid)); 
    }
}

还可以阅读SerializationInfo 类,了解有关序列化和反序列化最常见类型的更多信息。

【讨论】:

  • 从 OP 谈论属性(而不是字段)的事实来看,我假设他们谈论的是 XmlSerializer。我认为这个答案没有帮助。
  • @Marc Gravell:是的,看起来你是对的。在我的辩护中,当我发布答案时,他在谈论 XmlSerializer 的事实并不明显,他只是在后来才进行了编辑。
【解决方案2】:

根据描述,我假设您使用的是XmlSerializer。事实是XmlSerializer 缺乏这方面的粒度。它不支持回调(这将允许您检测序列化)等。

选项:

  • 实现IXmlSerializable - 大量工作(使用XmlReader/XmlWriter),而且容易出错...除非迫不得已,否则不要这样做
  • 使用不同的序列化程序(例如DataContractSerializer,它也支持私有getter/setter 或字段,但不支持完整 xml 控件)-例如see here
  • 使用单独的 DTO;即在应用程序中使用带有私有 setter 的类,以及用于序列化的 separate(更简单的)类(公共 get/set),可能在它们之间使用隐式转换运算符
  • 公开获取/设置,不用担心

我想我会选择 DTO 选项;它保留了对 xml 的简单但完整的格式控制,而且工作量不大。

using System;
using System.Xml.Serialization;
[XmlType("foo"), XmlRoot("foo")]
public class FooDto {
    [XmlAttribute("bar")]
    public string Bar { get; set; }

    public static implicit operator Foo(FooDto value) {
        return value == null ? null :
            new Foo(value.Bar);
    }
    public static implicit operator FooDto(Foo value) {
        return value == null ? null :
            new FooDto { Bar = value.Bar };
    }
}
public class Foo {
    private readonly string bar;
    public Foo(string bar) { this.bar = bar; }
    public string Bar { get { return bar; } }
}
static class Program {
    static void Main() {
        Foo foo = new Foo("abcdefg");
        FooDto dto = foo;
        new XmlSerializer(dto.GetType()).Serialize(
            Console.Out, dto);
    }
}

【讨论】:

  • 大声笑.. 你比我早 11 分钟发布 :) 在我编辑答案和测试示例时,不知道你已经发布了。多么惊喜!你赢了,马克 :)
【解决方案3】:

首先,我认为您的代码无法编译。您需要转换为(Foo)。您还缺少一个“使用”块:

public static Foo DeserializeFromFile(string sFilespec)
{
        Xml.Serialization.XmlSerializer oSerializer = new Xml.Serialization.XmlSerializer(typeof(Foo));
        using (System.IO.FileStream oStream = new System.IO.FileStream(sFilespec, IO.FileMode.Open)) {
            return (Foo) oSerializer.Deserialize(oStream);
        }
}

更重要的是,除非您实现 IXmlSerializable 接口并进行自己的反序列化,否则您在这里运气不佳。反序列化Id属性时,可以直接设置_Id字段,不使用属性。

【讨论】:

    【解决方案4】:

    Drjokepu 描述的方式似乎是正确的做法。同样,如果出于任何原因/约束/要求必须使用 XmlSerialization,则应实现 IXmlSerializable 接口。但是,实现 IXmlSerializable 可能不如实现 ISerializable 直观。

    在这种情况下,您可能想尝试以下技巧作为解决方法:)。

    1. 将 Foo.Id 属性设为只读
    2. 创建一个容器对象,Boo。 Boo 只包含要序列化的相同类型的成员变量。在此示例中,Boo 将具有带有公共 setter 和 getter 的 Id 属性。 Boo 不应包含 Foo 拥有的任何成员方法。
    3. 使用 Boo 进行 xml 序列化和反序列化,使用 Foo 处理业务逻辑。逻辑。

    参见下面的示例代码:

    using System;
    using System.IO;
    using System.Xml.Serialization;
    
    namespace SerializationSample
    {
        class Program
        {
            private const string STR_CtempSerilizationxml = @"c:\temp\Serilization.xml";
            static void Main(string[] args)
            {
                Foo foo = new Foo(Guid.NewGuid().ToString());
                Console.WriteLine("Foo.Id = " + foo.Id);
    
                SerializeToFile(foo, STR_CtempSerilizationxml);
    
                Foo fooDeserialized = DeserializeFromFile(STR_CtempSerilizationxml);
    
                Console.WriteLine("Foo.Id = " + fooDeserialized.Id);
    
            }
    
            private static void SerializeToFile(Foo foo, string sFilespec)
            {
                XmlSerializer iSerializer = new XmlSerializer(typeof(Boo));
                using (FileStream stream = new FileStream(sFilespec, FileMode.Create))
                {
                    iSerializer.Serialize(stream, new Boo(foo));
                }
            }
    
            public static Foo DeserializeFromFile(string sFilespec)
            {
                XmlSerializer oSerializer = new XmlSerializer(typeof(Boo));
                using (System.IO.FileStream oStream = new FileStream(sFilespec, FileMode.Open))
                {
                    return new Foo(((Boo)oSerializer.Deserialize(oStream)).Id);
                }
            }
        }
    
        public class Foo
        {
            private readonly string _Id;
            public string Id 
            { 
                get { return _Id; }
            }
    
            public Foo(string id) { _Id = id; }      
        }
    
        public class Boo
        {
            public string Id { get; set; }
    
            public Boo(Foo foo) { Id = foo.Id; }
    
            public Boo() { }
        }
    }
    

    【讨论】:

      【解决方案5】:

      如果只设置一次,可以加个bool ...

      private string _Id = system.Guid.NewGuid().ToString();
      private bool _IdSet = false;
      public string Id {
          get { return _Id; }
          set 
          { 
              if(!_IdSet)
              {
                  _Id = value;
                  _IdSet = true;
              }
              // else throw exception?
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-10-12
        • 2020-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多