Сериализация .NET - JSON перечисления как строки

у меня есть класс, который содержит элемент enum свойства, и при сериализации объекта с помощью JavaScriptSerializer мой результат JSON содержит целочисленное значение перечисления, а не его string "имя". Есть ли способ получить перечисление как string в моем json без необходимости создавать пользовательский JavaScriptConverter? Возможно, есть атрибут, который я мог бы украсить enum определение или свойство объекта, с?

пример:

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }
    Gender Gender { get; set; }
}

желаемый json результат:

{ "Age": 35, "Gender": "Male" }

20 ответов


нет нет специального атрибута, который вы можете использовать. JavaScriptSerializer сериализует enums к их числовым значениям, а не к их строковому представлению. Вам нужно будет использовать пользовательскую сериализацию для сериализации enum как его имя вместо числового значения.

Edit: Как отметил @OmerBakhari JSON.net охватывает этот вариант использования (через атрибут [JsonConverter(typeof(StringEnumConverter))]) и многие другие не обрабатываются встроенными сериализаторами .net. вот ссылка сравнения характеристик и функции сериализаторы.


я обнаружил, что Json.NET обеспечивает точное функциональность, я ищу с :

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public Gender Gender { get; set; }

более подробная информация доступна на StringEnumConverter документация.


добавьте ниже к глобальному.asax для сериализации JSON перечисления c# как string

  HttpConfiguration config = GlobalConfiguration.Configuration;
            config.Formatters.JsonFormatter.SerializerSettings.Formatting =
                Newtonsoft.Json.Formatting.Indented;

            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add
                (new Newtonsoft.Json.Converters.StringEnumConverter());

@Iggy answer устанавливает сериализацию JSON перечисления c# как строку только для ASP.NET (Web API и так далее).

но чтобы он работал также с специальной сериализацией, добавьте следующее в свой класс start (например, Global.событий Application_Start эйсакс)

//convert Enums to Strings (instead of Integer) globally
JsonConvert.DefaultSettings = (() =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter { CamelCaseText = true });
    return settings;
});

дополнительная информация на Json.NET страница

кроме того, чтобы иметь член перечисления для сериализации/десериализации в/из определенного текста, используйте

, например:
public enum time_zone_enum
{
    [EnumMember(Value = "Europe/London")] 
    EuropeLondon,

    [EnumMember(Value = "US/Alaska")] 
    USAlaska
}

Я не смог изменить исходную модель, как в верхнем ответе (@ob.), и я не хотел регистрировать его глобально, как @Iggy. Поэтому я объединил https://stackoverflow.com/a/2870420/237091 и @Iggy's https://stackoverflow.com/a/18152942/237091 чтобы разрешить настройку преобразователя перечисления строк во время самой команды SerializeObject:

Newtonsoft.Json.JsonConvert.SerializeObject(
    objectToSerialize, 
    Newtonsoft.Json.Formatting.None, 
    new Newtonsoft.Json.JsonSerializerSettings()
    {
        Converters = new List<Newtonsoft.Json.JsonConverter> {
            new Newtonsoft.Json.Converters.StringEnumConverter()
        }
    })

это легко сделать, добавив ScriptIgnore до Gender свойство, в результате чего оно не будет сериализовано и добавлено GenderString имущества,тут сделать по частям:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

    string GenderString { get { return Gender.ToString(); } }
}

эта версия Стивена ответ не изменяет имя в JSON:

[DataContract(
    Namespace = 
       "http://schemas.datacontract.org/2004/07/Whatever")]
class Person
{
    [DataMember]
    int Age { get; set; }

    Gender Gender { get; set; }

    [DataMember(Name = "Gender")]
    string GenderString
    {
        get { return this.Gender.ToString(); }
        set 
        { 
            Gender g; 
            this.Gender = Enum.TryParse(value, true, out g) ? g : Gender.Male; 
        }
    }
}

вот ответ для newtonsoft.в JSON

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}

вот простое решение, которое сериализует серверное перечисление C# в JSON и использует результат для заполнения клиентской стороны <select> элемент. Это работает как для простых перечислений, так и для перечислений bitflag.

Я включил сквозное решение, потому что я думаю, что большинство людей, желающих сериализовать перечисление C# в JSON, также, вероятно, будут использовать его для заполнения <select> падение-вниз.

вот:

Пример Перечислимого

public enum Role
{
    None = Permission.None,
    Guest = Permission.Browse,
    Reader = Permission.Browse| Permission.Help ,
    Manager = Permission.Browse | Permission.Help | Permission.Customise
}

A сложное перечисление, использующее побитовые ORs для создания системы разрешений. Поэтому вы не можете полагаться на простой индекс [0,1,2..] для целочисленного значения перечисления.

Сервер На C#

Get["/roles"] = _ =>
{
    var type = typeof(Role);
    var data = Enum
        .GetNames(type)
        .Select(name => new 
            {
                Id = (int)Enum.Parse(type, name), 
                Name = name 
            })
        .ToArray();

    return Response.AsJson(data);
};

приведенный выше код использует структуру NancyFX для обработки запроса Get. Он использует вспомогательный метод-но не волнуйтесь, вы можете использовать любой стандартный форматер JSON, поскольку перечисление уже проецируется в простой анонимный тип, готовый для сериализация.

сгенерированный JSON

[
    {"Id":0,"Name":"None"},
    {"Id":2097155,"Name":"Guest"},
    {"Id":2916367,"Name":"Reader"},
    {"Id":4186095,"Name":"Manager"}
]

Сторона Клиента-CoffeeScript

fillSelect=(id, url, selectedValue=0)->
    $select = $ id
    $option = (item)-> $ "<option/>", 
        {
            value:"#{item.Id}"
            html:"#{item.Name}"
            selected:"selected" if item.Id is selectedValue
        }
    $.getJSON(url).done (data)->$option(item).appendTo $select for item in data

$ ->
    fillSelect "#role", "/roles", 2916367

HTML-код перед

<select id="role" name="role"></select>

HTML после

<select id="role" name="role">
    <option value="0">None</option>
    <option value="2097155">Guest</option>
    <option value="2916367" selected="selected">Reader</option>
    <option value="4186095">Manager</option>
</select>

комбинация ответов Омера Бухари и Ури также является моим решением, поскольку значения, которые я хочу предоставить, обычно отличаются от того, что у меня есть в моем перечислении, особенно, что я хотел бы иметь возможность изменить мои перечисления, если мне нужно.

поэтому, если кто-то заинтересован, это что-то вроде этого:

public enum Gender
{
   [EnumMember(Value = "male")] 
   Male,
   [EnumMember(Value = "female")] 
   Female
}

class Person
{
    int Age { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}

вы можете создать JsonSerializerSettings с вызовом JsonConverter.SerializeObject, как показано ниже:

var result = JsonConvert.SerializeObject
            (
                dataObject,
                new JsonSerializerSettings
                {
                    Converters = new [] {new StringEnumConverter()}
                }
            );

вы также можете добавить конвертер в свой JsonSerializer Если вы не хотите использовать :

string SerializedResponse = JsonConvert.SerializeObject(
     objToSerialize, 
     new Newtonsoft.Json.Converters.StringEnumConverter()
); 

это будет работать для каждого enum он видит во время этой сериализации.


Для .Net Core Web Api : -

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
    ...
}

заметил, что нет ответа для сериализации, когда есть атрибут Description.

вот моя реализация, которая поддерживает атрибут Description.

public class CustomStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType() as Type;

        if (!type.IsEnum) throw new InvalidOperationException("Only type Enum is supported");
        foreach (var field in type.GetFields())
        {
            if (field.Name == value.ToString())
            {
                var attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
                writer.WriteValue(attribute != null ? attribute.Description : field.Name);

                return;
            }
        }

        throw new ArgumentException("Enum not found");
    }
}

перечисление:

public enum FooEnum
{
    // Will be serialized as "Not Applicable"
    [Description("Not Applicable")]
    NotApplicable,

    // Will be serialized as "Applicable"
    Applicable
}

использование:

[JsonConverter(typeof(CustomStringEnumConverter))]
public FooEnum test { get; set; }

Это старый вопрос, но я думал, что внесу свой вклад на всякий случай. В моих проектах я использую отдельные модели для любых запросов Json. Модель обычно имеет то же имя, что и объект домена с префиксом "Json". Модели сопоставляются с помощью AutoMapper. Если модель json объявит строковое свойство, которое является перечислением в классе домена, AutoMapper разрешит его строковое представление.

Если вам интересно, мне нужны отдельные модели для сериализованных классов Json потому что встроенный сериализатор придумывает циклические ссылки в противном случае.

надеюсь, это кому-то поможет.


вы можете фактически использовать JavaScriptConverter для выполнения этого со встроенным JavaScriptSerializer. Преобразуя перечисление в Uri, вы можете кодировать его как строку.

Я описал, как это сделать для дат, но его можно использовать и для перечислений.

http://blog.calyptus.eu/seb/2011/12/custom-datetime-json-serialization/


на всякий случай, если кто-нибудь сочтет вышеизложенное недостаточным, я в конечном итоге решил эту перегрузку:

JsonConvert.SerializeObject(objToSerialize, Formatting.Indented, new Newtonsoft.Json.Converters.StringEnumConverter())

ASP.NET основной способ:

public class Startup
{
  public IServiceProvider ConfigureServices(IServiceCollection services)
  {
    services.AddMvc().AddJsonOptions(options =>
    {
      options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
    });
  }
}

https://gist.github.com/regisdiogo/27f62ef83a804668eb0d9d0f63989e3e


Я собрал все части этого решения, используя Newtonsoft.Json библиотека. Он устраняет проблему перечисления, а также делает обработку ошибок намного лучше, и он работает в службах IIS. Это довольно много кода, поэтому вы можете найти его на GitHub здесь: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

вы должны добавить некоторые записи в вашем Web.config чтобы заставить его работать, вы можете увидеть пример файла здесь: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config


new JavaScriptSerializer().Serialize(  
    (from p   
    in (new List<Person>() {  
        new Person()  
        {  
            Age = 35,  
            Gender = Gender.Male  
        }  
    })  
    select new { Age =p.Age, Gender=p.Gender.ToString() }  
    ).ToArray()[0]  
);