带有新枚举值的XmlSerializer

时间:2009-10-25 16:20:13

标签: c# xml-serialization

我们在项目中广泛使用xml序列化/反序列化来在多个应用程序之间传递数据。我们有一个常见的xsd,我们生成c#类,然后使用XmlSerializer从xml转到对象并返回。

我们遇到的问题是,当一个应用程序更新为添加新的枚举值但另一个应用程序尚未更新时。现在,未更新的应用程序尝试反序列化xml并失败,因为它不知道新的枚举。

如果我们有app1和app2,那么现场工作正常,那么app2会在xsd中使用新的枚举值进行更新,并在字段中更新到客户端。突然app1中断,因为它不知道enum,app1可能甚至不使用该枚举字段,对app1没有影响,但它仍然会中断。

有没有任何已知方法可以解决这个问题。基本上我想要做的是定义在找不到枚举时做什么,使用默认值或者枚举为nullible类型并将其设置为null。

XmlSerializer和DataContractSerializer抛出异常都是这种情况。

我已经查看了自定义xml序列化项目YAXLib(http://www.codeproject.com/KB/XML/yaxlib.aspx)这也引发了异常,但是有源代码并且可以更改。该项目使用不同的属性属性,需要进行相当多的更改,但可能是可行的。

任何其他建议。

7 个答案:

答案 0 :(得分:11)

不幸的是,无法控制枚举值的反序列化...作为解决方法,您可以将枚举值序列化为字符串:

[XmlIgnore]
public MyEnum MyProperty { get; set; }

[XmlElement("MyProperty")]
public string MyPropertyAsString
{
    get
    {
        return EnumToString(MyProperty);
    }
    set
    {
        MyProperty = StringToEnum<MyEnum>(value);
    }
}

public T StringToEnum<T>(string stringValue)
{
    // Manually convert the string to enum, ignoring unknown values
}

public string EnumToString<T>(T enumValue)
{
    // Convert the enum to a string
}

答案 1 :(得分:7)

为了将来参考,我认为最好的方法是使用XmlEnumAttribute告诉XMLSerializer每个枚举值对序列化和反序列化的名称。

public enum EmployeeStatus
{
   [XmlEnum(Name = "Single")]
   One,
   [XmlEnum(Name = "Double")]
   Two,
   [XmlEnum(Name = "Triple")]
   Three
}

答案 2 :(得分:2)

我也一直在努力解决这个问题,并且我找到了使用XmlAttributeOverrides的部分解决方案。根据MS文档:

  

您可以通过创建XmlEnumAttribute类的实例并将其分配给XmlAttributes对象的XmlEnum属性来覆盖XmlEnumAttribute的Name属性值。有关详细信息,请参阅XmlAttributeOverrides类。

所以我这样做了:

    XmlAttributeOverrides attrOverrides =
       new XmlAttributeOverrides();
    XmlAttributes attrs = new XmlAttributes();
    XmlEnumAttribute _enum = new XmlEnumAttribute();
    _enum.Name = "new value";
    attrs.XmlEnum = _enum;
    attrOverrides.Add(typeof(EnumType), "OldValue", attrs);
    XmlSerializer s =
        new XmlSerializer(typeof(MyType), attrOverrides);

    FileStream fs = new FileStream(filename, FileMode.Open);
    MyType newObj = (MyType)s.Deserialize(fs);

我用这种方法找到的唯一问题是我无法为单个枚举指定多个值...

答案 3 :(得分:2)

我最近遇到了与DataContractSerializer相同的问题。基本上,枚举反序列化过程在这方面是不可原谅的,因此几乎不可能管理向后兼容性。

作为解决方法,我决定使用支持字段并自行处理枚举转换。

[DataMember(Name="AddressType")]
private string _addressType { get; set; }

public AddressType AddressType
{
    get
    {
        AddressType result;
        Enum.TryParse(_addressType, out result);
        return result;
    }
}

私有字段可以由DataContractSerializer反序列化,但XmlSerializer需要一个公共字段。

答案 4 :(得分:1)

您可以让生产应用程序知道消费应用程序的多个版本,并让它为每个版本使用不同的名称空间,包括XML和C#。需要在应用程序之间进行一些协调,以商定他们将遵循的模式,并且生产应用程序还有责任继续向后兼容所有可能的活动消费者应用程序。

答案 5 :(得分:0)

使用c#自定义序列化和对象的版本控制;这将允许您处理一个应用程序更新而另一个不是

时出现的各种情况

答案 6 :(得分:0)

更好的方法是在2个应用之间共享序列化/反序列化代码 但如果不可能,您可以使用XmlAttributeOverrides来广泛控制xml的反序列化方式。例如,在我们想要反序列化的一些旧版本的xml下面

    ...
    <Shape xsi:type="Shape">
       <Style>1</Style>
    <Shape/>
    ...

在新版本样式中,属性被编码为Style

类型的枚举
    public enum Style
    {
       Style1,
       Style2,
       Style3,
    }

如果我们尝试反序列化旧的xml,我们将获得异常。 我们可以使用XmlAttributeOverrides来避免这个

    var overrides = new XmlAttributeOverrides();
    overrides.Add(typeof(Style), "Style1", new XmlAttributes { XmlEnum = new 
        XmlEnumAttribute("1" ) } );
    overrides.Add(typeof(Style), "Style2", new XmlAttributes { XmlEnum = new      
        XmlEnumAttribute("2") });
    overrides.Add(typeof(Style), "Style3", new XmlAttributes { XmlEnum = new 
        XmlEnumAttribute("3") });

    var xmlSerializer = new XmlSerializer(typeof(ContentRootType),   
        overrides);
    var content = xmlSerializer.Deserialize(xmlReader) as ContentRootType;