如何使用 C# 中的 System.Xml.Serialization 使对象不可变但可序列化?

Posted

技术标签:

【中文标题】如何使用 C# 中的 System.Xml.Serialization 使对象不可变但可序列化?【英文标题】:How to make object immutable yet serializable with System.Xml.Serialization in C#? 【发布时间】:2021-12-11 13:59:48 【问题描述】:

在我的应用程序中,我有很多要序列化的 XML,所以我创建了很多模型来序列化。我使用 System.Xml.Serialization,因此它需要无参数构造函数和对属性的公共访问权限才能将反序列化数据写入其中。

现在,我想实现备忘录模式以提供撤消和重做功能,在这种情况下,能够拥有不可变对象会很好,因此不会有任何与我更改某些属性而不是相关的错误创建新实例。

问题是我没有看到任何可以同时为我提供两种解决方案的解决方案 - 能够反序列化对象,然后为 Memento 模式提供不变性(嗯,有冰棒不变性,但我不确定)。创建单独的对象(记录左右),在程序完成反序列化后将我的反序列化对象转换为这些对象似乎是一种解决方案,但它有一个缺陷,即引入了许多新类型,这会消耗时间并增加以后的维护成本。

你们还有其他我可以使用的想法或模式吗?

【问题讨论】:

【参考方案1】:

我能够在this article 的帮助下创建一个解决方案。唯一需要注意的是它需要DataContractSerializer 而不是System.Xml.Serialization。这是我的实现的dotnetfiddle。

public class DataContractXmlSerializer<T>

    private readonly DataContractSerializer _dataContractSerializer;

    public DataContractXmlSerializer()
    
        _ = Attribute.GetCustomAttribute(typeof(T), typeof(DataContractAttribute))
        ?? throw new InvalidOperationException($"Type typeof(T) does not contain Attribute of type typeof(DataContractAttribute).");

        _dataContractSerializer = new DataContractSerializer(typeof(T));
    

    public T Deserialize(string xml)
    
        var xmlData = Encoding.UTF8.GetBytes(xml);
        var xmlStream = new MemoryStream(xmlData);

        using (XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(xmlStream, Encoding.UTF8, new XmlDictionaryReaderQuotas(), null))
        
            return (T)_dataContractSerializer.ReadObject(reader);
        
    

    public string Serialize(T obj)
    
        using (MemoryStream memoryStream = new MemoryStream())
        
            _dataContractSerializer.WriteObject(memoryStream, obj);
            return Encoding.UTF8.GetString(memoryStream.ToArray());
         
    


更新如果您必须使用System.Xml.Serialization,我建议使用分层方法在可变模型和不可变模型之间进行转换。这是我的dotnetfiddle 这种方法。

public class ModelXmlSerializer : ImmutableXmlSerializer<ImmutableModel, Model>

    protected override ImmutableModel ConvertToImmutable(Model mutable)
    
        return new ImmutableModel(mutable);
    
    
    protected override Model ConvertToMutable(ImmutableModel immutable)
    
        return new Model(immutable);
    


public abstract class ImmutableXmlSerializer<TImmutable, TMutable>

    private readonly MyXmlSerializer<TMutable> _xmlSerializer;

    public ImmutableXmlSerializer()
    
        _xmlSerializer = new MyXmlSerializer<TMutable>();
    

    public TImmutable Deserialize(string xml)
    
        return ConvertToImmutable(_xmlSerializer.Deserialize(xml));
    

    public string Serialize(TImmutable obj)
    
        return _xmlSerializer.Serialize(ConvertToMutable(obj));
    
    
    protected abstract TImmutable ConvertToImmutable(TMutable mutable);
    protected abstract TMutable ConvertToMutable(TImmutable immutable);


public class MyXmlSerializer<T>

    private readonly XmlSerializer _xmlSerializer;

    public MyXmlSerializer()
    
        _xmlSerializer = new XmlSerializer(typeof(T));
    

    public T Deserialize(string xml)
    
        using (TextReader reader = new StringReader(xml))
        
            return (T)_xmlSerializer.Deserialize(reader);
        
    

    public string Serialize(T obj)
    
        var builder = new StringBuilder();
        using (TextWriter writer = new StringWriter(builder))
        
            _xmlSerializer.Serialize(writer, obj);
        
        
        return builder.ToString();
    

此方法不太通用,因为它需要您为每种模型类型创建映射逻辑。您可以使用 AutoMapper 之类的工具简化该过程,但我更喜欢手动执行此操作。

【讨论】:

不幸的是,我无法控制 XML 格式,并且它使用了很多属性而不是元素(元素用于其他东西),所以这个解决方案不是一个选项。无论如何感谢您的回复。 你还在工作吗?如果它仍然可以使您受益,我可以尝试另一个想法。 是的,我还在做这个,我可能会做一段时间,还有一些工作要做。 我添加了对分层方法的更新,该方法与 System.Xml.Serialization 一起使用以在不可变对象和可变对象之间进行转换。我试图通过使用继承使其更通用,但 XML 序列化程序似乎不喜欢我尝试的任何东西。

以上是关于如何使用 C# 中的 System.Xml.Serialization 使对象不可变但可序列化?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 检查 datagridview 中的多个复选框

如何使用 C# 获取 Access 数据库中的列列表?

如何使用 c# 更新访问中的真/假字段?

如何使用 C# 中的 Windows 服务跟踪给定进程是不是引发异常

如何使用c#更改波形文件中的频率(音高)和幅度

如何使用 C# 找出安装在 SQL Server 中的 SSIS?