Як мені серіалізувати значення переліку як int?


75

Я хочу серіалізувати моє значення enum як int, але я отримую лише ім'я.

Ось мій (зразок) клас та перелік:

public class Request {
    public RequestType request;
}

public enum RequestType
{
    Booking = 1,
    Confirmation = 2,
    PreBooking = 4,
    PreBookingConfirmation = 5,
    BookingStatus = 6
}

І код (лише для того, щоб бути впевненим, що я не роблю це неправильно)

Request req = new Request();
req.request = RequestType.Confirmation;
XmlSerializer xml = new XmlSerializer(req.GetType());
StringWriter writer = new StringWriter();
xml.Serialize(writer, req);
textBox1.Text = writer.ToString();

Ця відповідь (на інше питання), мабуть, вказує на те, що перелічення повинні серіалізуватися на ints за замовчуванням, але, схоже, цього не робить. Ось мій результат:

<?xml version="1.0" encoding="utf-16"?>
<Request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <request>Confirmation</request>
</Request>

Я зміг серіалізувати як значення, поставивши атрибут "[XmlEnum (" X ")]" на кожне значення, але це просто здається неправильним.

Відповіді:


71

Здебільшого люди хочуть імен, а не ім’я. Ви можете додати властивість shim для цього?

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

[XmlElement("Foo")]
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public int FooInt32 {
    get {return (int)Foo;}
    set {Foo = (MyEnum)value;}
}

Або ви могли б використати IXmlSerializable, але це багато роботи.


Щоб нам було зрозуміло - те, що робить фрагмент, це повідомляти XmlSerializer - ІГНОРУВАТИ властивість MyEnum. І СЕРІАЛІЗУЙТЕ Властивість FooInt32, яка просто перетворює MyEnum проп на значення Int32. Це підійде вам ідеально.
— Cheeso

Це хороше рішення при посиланні на Enum зі сторонніх бібліотек.
— CtrlDot

Слід також зазначити, що властивість FooInt32 серіалізовано як ім'я Foo. Ця відповідь мені найбільше подобається, тому що якщо у вас є десятки значень у вашому переліку, вам потрібно зробити це лише один раз.
— intrepidis

Я також успішно використовував це в службі WebHTTP (JSON), з єдиною зміною, що атрибут [XmlElement ("Foo")] замінений на [DataMember (Name = "Foo")]
— Ден Парсонсон,

146

Найпростіший спосіб - використовувати атрибут [XmlEnum] так:

[Serializable]
public enum EnumToSerialize
{
    [XmlEnum("1")]
    One = 1,
    [XmlEnum("2")]
    Two = 2
}

Це буде серіалізуватися в XML (скажімо, що батьківським класом є CustomClass) приблизно так:

<CustomClass>
  <EnumValue>2</EnumValue>
</CustomClass>

5
Можна сперечатися, чи це насправді менше праці, ніж прийнята відповідь. Більше значень переліку = більше обслуговування.
— youwhut

5
+1 Це також мій найкращий метод - навіщо створювати непотрібне властивість shim, коли кілька додаткових директив дозволять вам (де) серіалізувати Enum належним чином.
— CJM

1
Якщо ви явно не визначите всі комбінації. Для переліків малих прапорів це варіант.
— xr280xr

2
+1 Особисто я вважаю, що це більш елегантне рішення, ніж прийнята відповідь. Таким чином, також простіше серіалізувати словники, де значення переліку використовуються як ключі.
— Алекс

1
Чудові речі. Я рекомендую скинути Serializableатрибут, хоча це не потрібно для серіалізації Xml.
— липень

13

Будь ласка, перегляньте повний приклад консольної програми нижче, щоб дізнатись про цікавий спосіб досягти того, що ви шукаєте, використовуючи DataContractSerializer:

using System;
using System.IO;
using System.Runtime.Serialization;

namespace ConsoleApplication1
{
    [DataContract(Namespace="petermcg.wordpress.com")]
    public class Request
    {
        [DataMember(EmitDefaultValue = false)]
        public RequestType request;
    }

    [DataContract(Namespace = "petermcg.wordpress.com")]
    public enum RequestType
    {
        [EnumMember(Value = "1")]
        Booking = 1,
        [EnumMember(Value = "2")]
        Confirmation = 2,
        [EnumMember(Value = "4")]
        PreBooking = 4,
        [EnumMember(Value = "5")]
        PreBookingConfirmation = 5,
        [EnumMember(Value = "6")]
        BookingStatus = 6
    }

    class Program
    {
        static void Main(string[] args)
        {
            DataContractSerializer serializer = new DataContractSerializer(typeof(Request));

            // Create Request object
            Request req = new Request();
            req.request = RequestType.Confirmation;

            // Serialize to File
            using (FileStream fileStream = new FileStream("request.txt", FileMode.Create))
            {
                serializer.WriteObject(fileStream, req);
            }

            // Reset for testing
            req = null;

            // Deserialize from File
            using (FileStream fileStream = new FileStream("request.txt", FileMode.Open))
            {
                req = serializer.ReadObject(fileStream) as Request;
            }

            // Writes True
            Console.WriteLine(req.request == RequestType.Confirmation);
        }
    }
}

Зміст request.txt наступний після виклику WriteObject:

<Request xmlns="petermcg.wordpress.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <request>2</request>
</Request>

Вам знадобиться посилання на збірку System.Runtime.Serialization.dll для DataContractSerializer.


У запитанні вже зазначається використання [XmlEnum ("...")], XmlSerializer, еквівалентний [EnumMember ("...")] - тому я не впевнений, що це додає щось, про що OP ще не знає.
— Марк Гравелл

1
Окрім цього - оскільки DataContractSerializer підтримує приватних членів, простішим підходом буде приватна властивість shim, яка перетворюється між intі переліченням.
— Марк Гравелл

Так, я помітив, що re: XmlEnum дякую, але, як я вже кажу, я думаю, що це цікаве рішення питання. Яке рішення є `` простішим '', є суб'єктивним і, в кінцевому рахунку, залежить від запитувача. Погоджено: з використанням підхідного підходу DataContractSerializer та підтримка приватних членів - це шлях
— Пітер Макґ

1
@MarcGravell, @Peter McGrattan: так що тут щось не так із таким використанням [EnumMember(Value = "1")]? Або ми завжди повинні йти з властивістю "shim", як пропонує Марк Дж?
— VoodooChild

2
using System.Xml.Serialization;

public class Request
{    
    [XmlIgnore()]
    public RequestType request;

    public int RequestTypeValue
    {
      get 
      {
        return (int)request;
      } 
      set
      {
        request=(RequestType)value; 
      }
    }
}

public enum RequestType
{
    Booking = 1,
    Confirmation = 2,
    PreBooking = 4,
    PreBookingConfirmation = 5,
    BookingStatus = 6
}

Зазначений вище підхід спрацював для мене.


0

Погляньте на клас System.Enum. Метод Parse перетворює рядок або представлення int в об'єкт Enum, а метод ToString - об'єкт Enum у рядок, який можна серіалізувати.


3
Хоча все правда, це не стосується того, як використовувати це прозоро під час серіалізації, що є справжньою проблемою.
— Марк Гравелл

0

Оскільки ви призначаєте явні непослідовні значення параметрам перерахування, я припускаю, що ви хочете мати можливість вказати більше одного значення за раз (двійкові прапори), тоді прийнята відповідь - ваш єдиний варіант. Перехід до попереднього замовлення | Підтвердження PreBookingConfirmation матиме ціле число 9, і серіалізатор не зможе десериалізувати його, додавши його властивістю shim, проте буде працювати добре. Або, можливо, ви просто пропустили значення 3 :)


2
Я щойно знайшов зручного друга з переліком [Flags], який дозволяє використовувати бітові прапори, і вони серіалізуються назад до правильних параметрів ...
— Адріан Давел,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.