XmlSerializer - 将xml文件迁移到新版本的最佳方法

时间:2014-04-01 12:21:12

标签: c# .net xml serialization xmlserializer

我们假设我有一个简单的类

public class Document
{
    public int Version { get; set; }
    public string Name { get; set; }
    public string Image { get; set; } // Base64 coded Bitmap object
}

现实世界的对象更复杂。我使用XmlSerializer.Serialize将实例保存到文件中。

图像中的内容以这种方式生成:

byte[] result = null;
using (var image = Bitmap.FromFile(@"filename"))
using (var stream = new MemoryStream())
{
    image.Save(stream, ImageFormat.Jpeg);
    result = stream.ToArray();
}
var content = Convert.ToBase64String(result);

现在我有一个突破性的变化。 将来我想保存原始图像数据(也作为base64)而不将其转换为jpg。

所以我的新对象将如下所示:

public class Document
{
    public int Version { get; set; }
    public string Name { get; set; }
    public string RawImageString { get; set; }
}

幸运的是,我已经为每个xml文件存储了一个版本属性(当前为1)。对于我可以

的新项目

现在我想知道是否有关于如何处理模型更改的best practices。 我正在考虑这种方法:

  • 仍在我的班级中定义属性ImageString,标记为过时。
  • 该属性只有一个setter但没有getter
  • 如果设置了ImageString,我只需更新RawImageString

    public class Document
    {
        public int Version { get; set; }
        public string Name { get; set; }
        public string RawImageString { get; set; }
        [Obsolete("Use RawImageString instead")]
        public string ImageString
        { 
            set
            {
                this.RawImageString = value; 
                this.Version = 2;
            }
        }
    }
    

这应该运作良好,但它需要我将遗留财产维持到永远。我更喜欢

// depending on the version property XmlSerializer should return a 
// different Document implementation
var serializer = new XmlSerializer(typeof(IDocument));
var document = (IDocument)serializer.Deserialize(reader);

当然我可以使用工厂方法实现这一点,但这需要两次读取。一个用于版本,第二个用于具体结果。

1 个答案:

答案 0 :(得分:2)

最终我这样解决了。

无论如何,使用静态方法创建Document。 现在我检查版本是否与当前版本匹配,如果没有则开始迁移。

    public const int CURRENT_VERSION = 2;
    public static DocumentOpen(string path)
    {
        var controller = new DocumentController();

        var item = controller.ReadXml(path);

        if (item.Version != CURRENT_VERSION)
        {
            var migrator = new DocumentMigrator(item, path);
            migrator.MigrateToLatestVersion();
        }

        return item;
    }

迁移器看起来像这样

public class DocumentMigrator
{

    private Document item;
    private String path;

    public DocumentMigrator(Documentitem, string path)
    {
        this.item = item;
        this.path = path;
    }

    public void MigrateToLatestVersion()
    {
        Migrate(Document.CURRENT_VERSION);
    }

    public void Migrate(int to)
    {
        Migrate(item.Version, to);
    }

    private void Migrate(int from, int to)
    {
        if (from < to)
        {
            while (item.Version < to)
                Up(item.Version + 1);
        }
        else if (from > to)
        {
            while (item.Version < to)
                Down(item.Version - 1);
        }
    }

    private void Down(int version)
    {
        throw new NotImplementedException();
    }

    private void Up(int version)
    {
        if (version == 2)
        {
            var stream = File.OpenRead(path);
            var serializer = new XmlSerializer(typeof(DocumentV1));
            var document = (DocumentV1)serializer.Deserialize(stream);

            this.item.RawImageString = document.ImageString;
        }
        else
        {
            throw new NotImplementedException();
        }

        this.item.Version = version;
    }

}

public class DocumentV1
{
    public string ImageString { get; set; }
}

我的想法是创建了一个帮助器类DocumentV1,它只包含我想要迁移的属性。甚至可以通过使用动力学或XElement来避免这种情况。

我执行升级并从原始类更新版本。向后迁移也可以在Down方法中实现,但目前不需要。