Як мені серіалізувати значення переліку як 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.