.net 仅在XML反序列化时设置

7hiiyaii  于 2023-05-08  发布在  .NET
关注(0)|答案(5)|浏览(180)

问题:

我有一个类,比如Foo,它实现了一个Id属性。Foo必须是可序列化的。在初始化Foo时,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#

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

解决思路:

唯一的解决方案似乎是在设置Foo.id时抛出一个运行时异常,但这将在反序列化过程中导致问题。因此,我们必须以某种方式确保只有在序列化器外部尝试SetFoo.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;
}
3ks5zfa0

3ks5zfa01#

我不确定我是否理解了你的问题,但是你可以尝试在你的类中实现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类,以获得有关序列化和反序列化最常见类型的更多信息。

zvms9eto

zvms9eto2#

根据描述,我假设您正在使用XmlSerializer。事实上,XmlSerializer缺乏这种粒度。它不支持回调(这将允许您检测序列化)等。
选项:

  • 实现IXmlSerializable-大量的工作(使用XmlReader/XmlWriter),并且容易出错...不到万不得已别做
  • 使用不同的序列化器(例如DataContractSerializer,它也支持私有getter/setter或字段,但不支持 full xml控制)-例如,请参见此处
  • 使用单独的DTO;例如,在应用程序中使用带有私有setter的类,以及用于序列化的单独(更简单)类(公共get/set),它们之间可能有一个隐式转换运算符
  • 让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);
    }
}
waxmsbnn

waxmsbnn3#

首先,我不认为你的代码编译。你需要转换为(Foo)。你还缺少了一个“using”块:

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字段,而不使用该属性。

yh2wf1be

yh2wf1be4#

Drjokepu描述的方式似乎是正确的做法。类似地,如果出于任何原因/约束/要求必须使用XmlSerialization,则应该实现IXmlSerializable接口。但是,实现IXmlSerializable可能不如实现ISerializable直观。
在这种情况下,您可能需要尝试以下技巧作为解决方法:)。
1.使Foo.id属性为只读
1.创建一个容器对象Boo。Boo只包含要序列化的相同类型的成员变量。在这个例子中,Boo将拥有Id属性,并带有公共setter和getter。Boo不应该包含Foo拥有的任何成员方法。
1.使用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() { }
    }
}
muk1a3rh

muk1a3rh5#

如果只设置一次,您可以添加一个布尔值。

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?
    }
}

相关问题