将可序列化模型维护到关于不同XSD版本的XML的良好实践

时间:2015-09-02 06:27:12

标签: c# xml validation xsd

假设我有针对架构验证的XML文件。此外,我有一个可以序列化到xml以上的模型,并根据XSD架构进行验证。

在应用程序生命周期中,我的模型可以更改,因此它的XSD版本更改。

E.g。版本1.0 有元素:

   <xs:complexType name="SomeConfig">
    <xs:all>
      <xs:element minOccurs="1" maxOccurs="1" name="ElementAlwaysValid" type="xs:boolean" />
      <xs:element minOccurs="0" maxOccurs="1" name="SomeElement" type="xs:string" />
    </xs:all>
  </xs:complexType>

然后在版本1.1中发现有人认为它是这样的:

  <xs:complexType name="SomeConfig">
    <xs:all>
      <xs:element minOccurs="1" maxOccurs="1" name="ElementAlwaysValid" type="xs:boolean" />
        <xs:element minOccurs="1" nillable="true" name="SomeElement" type="xs:string" />
    </xs:all>
  </xs:complexType>

然后在版本1.2中决定:不再需要someElement。

 <xs:complexType name="SomeConfig">
    <xs:all>
      <xs:element minOccurs="1" maxOccurs="1" name="ElementAlwaysValid" 
    </xs:all>
  </xs:complexType>

这里有兼容性问题。

一旦出现验证错误,架构需要元素&#34; SomeElement&#34;但它不存在。

另一次可能发生错误,架构不需要SomeElement但它存在。

有没有什么好的模式可以解决这些兼容的问题?

我考虑过转换器或XSLT,但也许有一些关于这个主题的好习惯。

1 个答案:

答案 0 :(得分:0)

例如,使用此常量类:

namespace Q32345998_XsdVersion
{
    public static class Constants
    {
        public const string XsdNamespace = "http://tempuri.org/XMLSchema10.xsd";
        public const string SomeConfigRootName = "sc";
    }
}

您可以根据版本将对象拆分为不同的命名空间。每个SomeConfig类都将使用以下属性进行修饰:

[Serializable]
[XmlType(Namespace = Constants.XsdNamespace)]
[XmlRoot(Constants.SomeConfigRootName, Namespace = Constants.XsdNamespace, IsNullable = false)]

然后通过显式/隐式转换运算符添加后向/前向合规性。因此:

namespace Q32345998_XsdVersion.v10
{
    public partial class SomeConfig
    {
        public bool ElementAlwaysValid { get; set; }
        public string SomeElement { get; set; }
    }
}
namespace Q32345998_XsdVersion.v11
{
    public partial class SomeConfig
    {
        public bool ElementAlwaysValid { get; set; }

        [XmlElement(IsNullable = true)]
        public string SomeElement { get; set; }

        public static explicit operator v10.SomeConfig(v11.SomeConfig v11)
        {
            return new v10.SomeConfig() { ElementAlwaysValid = v11.ElementAlwaysValid, SomeElement = v11.SomeElement };
        }
    }
}
namespace Q32345998_XsdVersion.v12
{
    public partial class SomeConfig
    {
        public bool ElementAlwaysValid { get; set; }

        public static explicit operator v11.SomeConfig (v12.SomeConfig v12)
        {
            return new v11.SomeConfig() { ElementAlwaysValid = v12.ElementAlwaysValid, SomeElement = null };
        }
        public static explicit operator v10.SomeConfig(v12.SomeConfig v12)
        {
            return new v10.SomeConfig() { ElementAlwaysValid = v12.ElementAlwaysValid, SomeElement = null };
        }

    }
}

使用它:

// convert from v1.2 to v1.1 and 1.0
v12.SomeConfig sc12 = new v12.SomeConfig() { ElementAlwaysValid = true };
v11.SomeConfig sc11 = (v11.SomeConfig)sc12;
v10.SomeConfig sc10 = (v10.SomeConfig)sc12;
// convert from v1.1 to 1.0
sc11 = new v11.SomeConfig() { ElementAlwaysValid = true, SomeElement=string.Empty };
sc10 = (v10.SomeConfig)sc11;

虽然我认为对xsd和序列化对象进行版本设置会更优雅,或者在SomeConfig中添加一个版本属性。