如何从列表迁移到 class C# DataContract 序列化?

How do you migrate from a list to a class C# DataContract Serialization?

如何从布尔值列表中将配置迁移到新的 class?以前它使用 bool 列表,但该列表被滥用为 class,每个索引都有特定的含义,如字段。

我想将其从 List 迁移到 class,后者用作用于序列化目的的列表,但将普通字段公开给应用程序的其余部分。

如何编写 class ListEmulator 使其序列化为列表,而不引入新的 xml 标签?

旧代码

namespace
{
    [DataContract]
    public class Configuration
    {
        public const string FileName = "Configuration.xml";

        public Configuration()
        {
            AList = new List<bool>();
            AGuidList = new List<Guid>();
        }

        [DataMember]
        public List<Guid> AGuidList { get; set; }
        [DataMember]
        public List<bool> AList { get; set; }
    }
}

新代码。

namespace
{
    [DataContract]
    public class Configuration
    {
        public const string FileName = "Configuration.xml";

        public Configuration()
        {
            AListEmulator = new ListEmulator();
            AGuidList = new List<Guid>();
        }

        [DataMember]
        public List<Guid> AGuidList { get; set; }
        [DataMember]
        public ListEmulator AListEmulator { get; set; }
    }
}

public class ListEmulator
{

    public ListEmulator()
    {
        new ListEmulator(true, true, true, true);
    }

    public ListEmulator(bool item0, bool item1, bool item2, bool item3)
    {
        this.IsPlanned = item0;
        this.IsCompleted = item1;
        this.IsRemaining = item2;
        this.IsSerial = item3;
    }

    public bool IsPlanned { get; set; }
    public bool IsCompleted { get; set; }
    public bool IsRemaining { get; set; }
    public bool IsSerial { get; set; }
}

需要列表的原因是,当只有 1 个元素时需要移植旧的迁移代码,然后是 2、3、4,每个元素具有不同的默认值。如果不是因为我有原始格式的现有部署配置文件,可能是时候在 XML 中单独命名它们了。但是,我现在需要保留当前格式。为了迁移,我想知道如何完成上述操作。

一个选项 是让您的 ListEmulator 继承自 Collection<bool>,然后添加特定的命名属性以访问数组中的元素,如下所示:

public class ListEmulator : Collection<bool>
{
    const bool IsPlannedDefault = false;  // Change to the appropriate values.
    const bool IsCompletedDefault = false;
    const bool IsRemainingDefault = false;
    const bool IsSerialDefault = false;

    void AddAllDefaults()
    {
        // Customize the code here to upgrade old collections with fewer than 4 elements to the current 4-element format.
        if (Count < 1)
            Add(IsPlannedDefault);
        if (Count < 2)
            Add(IsCompletedDefault);
        if (Count < 3)
            Add(IsRemainingDefault);
        if (Count < 4)
            Add(IsSerialDefault);
    }

    public ListEmulator() { }

    public ListEmulator(bool item0, bool item1, bool item2, bool item3)
    {
        this.IsPlanned = item0;
        this.IsCompleted = item1;
        this.IsRemaining = item2;
        this.IsSerial = item3;
    }

    public bool IsPlanned { get { return this.ElementAtOrDefault(0, IsPlannedDefault); } set { AddAllDefaults(); this[0] = value; } }
    public bool IsCompleted { get { return this.ElementAtOrDefault(1, IsCompletedDefault); } set { AddAllDefaults(); this[1] = value; } }
    public bool IsRemaining { get { return this.ElementAtOrDefault(2, IsRemainingDefault); } set { AddAllDefaults(); this[2] = value; } }
    public bool IsSerial { get { return this.ElementAtOrDefault(3, IsSerialDefault); } set { AddAllDefaults(); this[3] = value; } }

    protected override void InsertItem(int index, bool item)
    {
        if (index > 3)
            throw new ArgumentOutOfRangeException("index > 3");
        base.InsertItem(index, item);
    }
}

然后在您的 Configuration 中只需将 List<bool> 替换为 ListEmulator,保留旧元素名称:

    [DataMember]
    public ListEmulator AList { get; set; }

因为此类型实现了 IEnumerable<T>DataContractSerializer 会将其序列化为集合而不是具有属性的对象。 (您可能想要更改 class 名称,因为此时它实际上不是列表 emulator。)但是,这仅在您不添加时有效默认构造函数中集合的任何初始值。

另一种选择 是向 Configuration 添加代理项 属性 来处理必要的转换,并将 ListEmulator AList 标记为不序列化:

[DataContract]
public class Configuration
{
    public const string FileName = "Configuration.xml";

    public Configuration()
    {
        AList = new ListEmulator();
        AGuidList = new List<Guid>();
    }

    [DataMember]
    public List<Guid> AGuidList { get; set; }

    [DataMember(Name = "AList")]
    bool[] AlistArray
    {
        get
        {
            return AList == null ? null : AList.ToArray();
        }
        set
        {
            AList = new ListEmulator(value);
        }
    }

    [IgnoreDataMember] // Do not serialize this property directly
    public ListEmulator AList { get; set; }
}

public class ListEmulator
{
    const bool IsPlannedDefault = false;  // Change to the appropriate values.
    const bool IsCompletedDefault = false;
    const bool IsRemainingDefault = false;
    const bool IsSerialDefault = false;

    public ListEmulator(IList<bool> list)
    {
        IsPlanned = list.ElementAtOrDefault(0, IsPlannedDefault);
        IsCompleted = list.ElementAtOrDefault(1, IsCompletedDefault);
        IsRemaining = list.ElementAtOrDefault(2, IsRemainingDefault);
        IsSerial = list.ElementAtOrDefault(3, IsSerialDefault);
    }

    public ListEmulator()
    {
        new ListEmulator(true, true, true, true);
    }

    public ListEmulator(bool item0, bool item1, bool item2, bool item3)
    {
        this.IsPlanned = item0;
        this.IsCompleted = item1;
        this.IsRemaining = item2;
        this.IsSerial = item3;
    }

    public bool IsPlanned { get; set; }
    public bool IsCompleted { get; set; }
    public bool IsRemaining { get; set; }
    public bool IsSerial { get; set; }

    public bool[] ToArray()
    {
        return new[] { IsPlanned, IsCompleted, IsRemaining, IsSerial };
    }
}

两个选项都使用以下扩展方法:

public static class ListExtensions
{
    public static T ElementAtOrDefault<T>(this IList<T> list, int index, T defaultValue)
    {
        if (index < 0)
            throw new ArgumentOutOfRangeException(string.Format("index = {0}", index));
        if (list == null || index >= list.Count)
            return defaultValue;
        return list[index];
    }
}