Converting xsd enums to C#
Asked Answered
M

4

6

I have an xsd file from which I am generating a C# class. In order to provide easier maintenance, I'd like to define an enumeration within the xsd file only so that when I have to change the enum, I only have to update it in one place. I know how to create the enum, but when the C# code is generated, I need the enum members to have custom values, so the result would be similar to:

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

Is there any way to write the xsd to accomplish this?

More answered 6/10, 2011 at 12:29 Comment(2)
Are you writing your own tool to generate the C# code, or is there some standard tool that generated C# code from XSD?Lysippus
Right now I'm using xsd.exe. I don't really have time to create my own tool.More
S
9

You can add annotations specific for code generation (this only works for svcutil, not for xsd.exe) to your xsd file. The xsd definition for your enum would be something like this:

<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>

These annotations allow you to explicitly define the numerical value of each enum value. You can find an example on this msdn page if you search for "EnumerationValue".

Update: John Saunders correctly states in his comment that this doesn't work if you use xsd.exe. However, if you use svcutil.exe to create the C# code, then the annotations will work.

Example of using svcutil.exe:

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

If you use svcutil instead of xsd.exe, then the generated code will by slightly different. The most important difference is that svcutil will generate attributes for DataContractSerialization instead of XmlSerialization.

Steppe answered 8/10, 2011 at 2:21 Comment(3)
-1: interesting link, and I didn't know about this feature; but if the OP is using XSD.EXE, then he's using XML Serialization, and EnumerationValue is a feature of the Data Contract Serializer.Bootlace
@John: Yes, it's true that this is for the data contract serializer, I thought it would also work for xsd.exe. However, if you use svcutil to create the code from the xsd file, then the annotations work.Steppe
The reason I'm using xsd is because I didn't realize I could do it any other way. While this may end up being more coding than I'd like to put into it, the answer is actually perfect for what I'm looking for.More
B
3

The concept of "enumeration" in XSD has nothing to do with the concept of "enum" in C#.

"enumeration" in XML Schema is a way of restricting the possible lexical values of a type to an enumerated list of values. For instance:

<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>

This type restricts the value space to the set of "Summer" months (July, August and September).

Clearly, this has no correspondence to a "enum" in C# or any other programming language I know.

Bootlace answered 8/10, 2011 at 1:2 Comment(2)
While I understand what you're saying, xsd.exe will convert an xsd enumeration into a C# enumeration, so there is some correlation. It just doesn't go as deep as I'd hoped it would.More
It will convert some xsd enumerations to enum. And even there, only with default values.Bootlace
L
2

I believe XSD enumerations are a more purist implementation of enumerations than .NET enumerations in the sense that they don't need and don't support numeric values associated with the enumerated names. Of course the generated code, being .NET code, will associate a numeric value with each named value internally, but this is an implementation detail that is not inherent to the nature of an enumeration as defined by the XSD standard. In this purist implementation of an enumeration, I believe the proper way to associate explicit numeric values with each enumerated name would be to define a separate collection/class that links enumerated values to numeric values. Or define additional enumerated values that represent the combined values that you support (NewInstallOrModify).

Edit:

Here's a sample of what a converter might look like.

// 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);
  }
}

Edit 2:

If your enum starts at 0 (as your example does) these two changes are necessary to my example:

Updated int converter:

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

The line after int log should be:

for (log = 0; value > 0; value >>= 1)
Lysippus answered 7/10, 2011 at 20:59 Comment(2)
Thanks. Do you have a link that would show a sample of a class that would link the values?More
@Drew Burchett: It depends on how you're going to use it, but I've added code that works for your example and assumes you want other enumerations to be similarly represented with powers of 2.Lysippus
P
0

Years later I think this was partially implemented. I'm not sure if you can provide specific values for each enum, but the XSD C# class generator is capable of converting XSD enums into C# enums from 0..n for n number enumeration values.

Example XSD with enum definition and used as a type.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:simpleType name="Status" final="restriction">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Enabled"></xs:enumeration>
      <xs:enumeration value="Disabled"></xs:enumeration>
      <xs:enumeration value="Running"></xs:enumeration>
    </xs:restriction>
  </xs:simpleType>
  <xs:element name="My">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:string">
          <xs:attribute type="Status" name="Status" use="optional"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
</xs:schema>

After running the converter (xsd My.xsd /classes)

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System.Xml.Serialization;

// 
// This source code was auto-generated by xsd, Version=4.8.3928.0.
// 


/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)]
public partial class My {
    
    private Status statusField;
    
    private bool statusFieldSpecified;
    
    private string valueField;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public Status Status {
        get {
            return this.statusField;
        }
        set {
            this.statusField = value;
        }
    }
    
    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool StatusSpecified {
        get {
            return this.statusFieldSpecified;
        }
        set {
            this.statusFieldSpecified = value;
        }
    }
    
    /// <remarks/>
    [System.Xml.Serialization.XmlTextAttribute()]
    public string Value {
        get {
            return this.valueField;
        }
        set {
            this.valueField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")]
[System.SerializableAttribute()]
public enum Status {
    
    /// <remarks/>
    Enabled,
    
    /// <remarks/>
    Disabled,
    
    /// <remarks/>
    Running,
}
Polygenesis answered 13/12, 2023 at 21:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.