将xsd枚举转换为c#

本文关键字:转换 枚举 xsd | 更新日期: 2023-09-27 18:10:46

我有一个xsd文件,我要从中生成一个c#类。为了提供更容易的维护,我想在xsd文件中定义一个枚举,这样当我必须更改枚举时,我只需要在一个地方更新它。我知道如何创建枚举,但是当生成c#代码时,我需要枚举成员具有自定义值,因此结果将类似于:

public enum SetupTypeEnum {
    None = 0,
    NewInstall = 1,
    Modify = 2,
    Upgrade = 4,
    Uninstall = 8
}

有什么方法可以编写xsd来完成这个任务吗?

将xsd枚举转换为c#

可以在xsd文件中添加特定于代码生成的注释(这只适用于svcutil,不适用于xsd.exe)。枚举的xsd定义应该是这样的:

<xs:simpleType name="SetupTypeEnum">
    <xs:restriction base="xs:string">
        <xs:enumeration value="None">
            <xs:annotation>
                <xs:appinfo>
                    <EnumerationValue xmlns="http://schemas.microsoft.com/2003/10/Serialization/">0</EnumerationValue>
                </xs:appinfo>
            </xs:annotation>
        </xs:enumeration>
        <xs:enumeration value="NewInstall">
            <xs:annotation>
                <xs:appinfo>
                    <EnumerationValue xmlns="http://schemas.microsoft.com/2003/10/Serialization/">1</EnumerationValue>
                </xs:appinfo>
            </xs:annotation>
        </xs:enumeration>
        ...
    </xs:restriction>
</xs:simpleType>

这些注释允许您显式地定义每个枚举值的数值。如果您搜索"EnumerationValue",可以在此msdn页面上找到示例。

更新: John Saunders在他的评论中正确地指出,如果你使用xsd.exe,这不起作用。但是,如果你使用svcutil.exe来创建c#代码,那么注释就可以工作了。

svcutil.exe使用示例:

svcutil /dconly "D:'test.xsd" /o:"D:'test.cs"

如果您使用svcutil而不是xsd.exe,那么生成的代码将略有不同。最重要的区别是svcutil将为DataContractSerialization而不是XmlSerialization生成属性。

XSD中的"枚举"概念与c#中的"枚举"概念没有任何关系。

XML Schema中的

"枚举"是一种将类型的可能词法值限制为枚举的值列表的方法。例如:

<xs:simpleType name="SummerMonth">
    <xs:restriction base="xs:gMonth">
        <xs:enumeration value="--07"/>
        <xs:enumeration value="--08"/>
        <xs:enumeration value="--09"/>
    </xs:restriction>
</xs:simpleType>

此类型将值空间限制为"夏季"月份(七月,八月和九月)的集合。

显然,这与c#或我所知道的任何其他编程语言中的"enum"没有对应关系。

我相信XSD枚举是一种比。net枚举更纯粹的枚举实现,因为它们不需要也不支持与枚举名称相关联的数值。当然,生成的代码(即。net代码)将在内部将一个数值与每个命名值关联起来,但这是一个实现细节,与XSD标准定义的枚举性质无关。在这个纯粹的枚举实现中,我认为将显式数值与每个枚举名称关联的正确方法是定义一个单独的集合/类,将枚举值链接到数值。或者定义额外的枚举值来表示您支持的组合值(NewInstallOrModify)。

编辑:

下面是一个转换器的示例。

// Generated code
public enum SetupTypeEnum
{
  None,
  NewInstall,
  Modify,
  Upgrade,
  Uninstall
}
// End generated code
public struct IntMappedEnum<T> where T : struct
{
  public readonly int originalValue;
  public IntMappedEnum(T value)
  {
     originalValue = (int)Enum.ToObject(typeof(T), value);
  }
  public IntMappedEnum(int originalValue)
  {
     this.originalValue = originalValue;
  }
  public static implicit operator int(IntMappedEnum<T> value)
  {
     return 1 << value.originalValue;
  }
  public static implicit operator IntMappedEnum<T>(T value)
  {
     return new IntMappedEnum<T>(value);
  }
  public static implicit operator IntMappedEnum<T>(int value)
  {
     int log;
     for (log = 0; value > 1; value >>= 1)
        log++;
     return new IntMappedEnum<T>(log);
  }
  public static explicit operator T(IntMappedEnum<T> value)
  {
     T result;
     Enum.TryParse<T>(value.originalValue.ToString(), out result);
     return result;
  }
}
class Program
{
  static void Main(string[] args)
  {
     SetupTypeEnum s = SetupTypeEnum.Uninstall;
     IntMappedEnum<SetupTypeEnum> c = s;
     int n = c;
     IntMappedEnum<SetupTypeEnum> c1 = n;
     SetupTypeEnum s1 = (SetupTypeEnum)c1;
     Console.WriteLine("{0} => {1} => {2}", s, n, s1);
  }
}

编辑2:

如果你的enum从0开始(就像你的例子一样),这两个改变是我的例子所必需的:

更新的int转换器:

public static implicit operator int(IntMappedEnum<T> value)
{
   return (value.originalValue == 0)?0:1 << (value.originalValue - 1);
}

int log之后的行应该是:

for (log = 0; value > 0; value >>= 1)