Преобразование перечислений xsd в C#

#c# #.net #xml #xsd

#c# #.net #xml #xsd

Вопрос:

У меня есть файл xsd, из которого я создаю класс C #. Чтобы упростить обслуживание, я хотел бы определить перечисление только в файле xsd, чтобы, когда мне нужно было изменить перечисление, мне нужно было обновлять его только в одном месте. Я знаю, как создать перечисление, но когда генерируется код C #, мне нужно, чтобы члены перечисления имели пользовательские значения, чтобы результат был похож на:

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

Есть ли какой-либо способ написать xsd для достижения этой цели?

Комментарии:

1. Вы пишете свой собственный инструмент для генерации кода C # или есть какой-то стандартный инструмент, который генерирует код C # из XSD?

2. Прямо сейчас я использую xsd.exe . У меня действительно нет времени на создание собственного инструмента.

Ответ №1:

Вы можете добавлять аннотации, специфичные для генерации кода (это работает только для svcutil, а не для xsd.exe ) в ваш xsd-файл. Определение 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>
  

Эти аннотации позволяют явно определять числовое значение каждого значения перечисления. Вы можете найти пример на этой странице msdn, если выполните поиск по «EnumerationValue».

Обновление: Джон Сондерс правильно указывает в своем комментарии, что это не сработает, если вы используете xsd.exe . Однако, если вы используете svcutil.exe чтобы создать код на C #, тогда аннотации будут работать.

Пример использования svcutil.exe :

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

Если вы используете svcutil вместо xsd.exe , то сгенерированный код будет немного отличаться. Наиболее важным отличием является то, что svcutil будут генерироваться атрибуты для DataContractSerialization вместо XmlSerialization.

Комментарии:

1. -1: интересная ссылка, и я не знал об этой функции; но если OP использует XSD.EXE , затем он использует сериализацию XML и EnumerationValue является функцией сериализатора контрактов на передачу данных.

2. @John: Да, это правда, что это для сериализатора контрактов с данными, я думал, что это тоже сработает xsd.exe . Однако, если вы используете svcutil для создания кода xsd-файл, аннотации будут работать.

3. Причина, по которой я использую xsd, заключается в том, что я не понимал, что могу сделать это любым другим способом. Хотя это может в конечном итоге потребовать больше кода, чем я хотел бы вложить в него, ответ на самом деле идеально подходит для того, что я ищу.

Ответ №2:

Понятие «перечисление» в XSD не имеет ничего общего с понятием «перечисление» в C #.

«перечисление» в XML-схеме — это способ ограничения возможных лексических значений типа перечисляемым списком значений. Например:

 <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 # или любом другом языке программирования, который я знаю.

Комментарии:

1. Хотя я понимаю, о чем вы говорите, xsd.exe преобразует перечисление xsd в перечисление C #, так что есть некоторая корреляция. Это просто не так глубоко, как я надеялся.

2. Он преобразует некоторые перечисления xsd в enum . И даже там, только со значениями по умолчанию.

Ответ №3:

Я считаю, что перечисления XSD являются более чистой реализацией перечислений, чем .ЧИСТЫЕ перечисления в том смысле, что они не нуждаются и не поддерживают числовые значения, связанные с перечисляемыми именами. Конечно, сгенерированный код, будучи .ЧИСТЫЙ код будет связывать числовое значение с каждым именованным значением внутри, но это деталь реализации, которая не присуща природе перечисления, как определено стандартом 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 resu<
     Enum.TryParse<T>(value.originalValue.ToString(), out result);
     return resu<
  }
}

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:

Если ваше перечисление начинается с 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)
  

Комментарии:

1. Спасибо. Есть ли у вас ссылка, которая показывала бы образец класса, который связывал бы значения?

2. @Drew Burchett: это зависит от того, как вы собираетесь его использовать, но я добавил код, который работает для вашего примера и предполагает, что вы хотите, чтобы другие перечисления были представлены аналогичным образом со степенями 2.