2

In my app I have a lot of XMLs to serialize from, so I have created quite a lot of models, to serialize to. I use System.Xml.Serialization, so it needs parameterless constructor and public access to properties to write deserialized data to.

Now, I want to implement Memento Pattern to provide Undo and Redo functionality, and in that case it would be good to be able to have immutable objects, so there would not be any bug related to that I changed some property instead of creating new instance.

The problem is I don't see any solution that would provide me both solutions at once - being able to deserialize objects and then provide immutability for Memento pattern (well, there is popsicle immutability but I am not sure about it). Creating separate objects (records or so) that my deserialized objects would be converted to after program is done with deserialization seems like solution, but it has a flaw o introducing a lot of new types that consume time and increase upkeep cost later.

Do you guys have other ideas or patterns that I could use?

4

1 回答 1

0

在本文的帮助下,我能够创建一个解决方案。唯一需要注意的是它需要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 之类的工具简化该过程,但我更喜欢手动执行此操作。

于 2021-12-11T22:40:50.473 回答