.NET - JSON-сериализация enum в виде строки

975

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

В качестве примера:

enum Gender { Male, Female }

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

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

{ "Age": 35, "Gender": "Male" }
  • 8
    Изменить на что? Ответ, получивший наибольшее количество голосов, на самом деле не отвечает на вопрос - да, он полезен в других контекстах, отсюда и при голосовании, но он бесполезен, если вы застряли с MS JavaScriptSerializer, как, по сути, и вы, если используете методы страницы и , самое главное, как того требует вопрос. Принятый ответ говорит, что это невозможно. Мой ответ, пока что-то вроде взлома, выполняет свою работу.
Теги:
serialization
enums
javascriptserializer

22 ответа

247
Лучший ответ

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

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

  • 5
    @Fabzter - ваше решение работало со мной, используя Json от Newtonsoft
  • 1
    @BornToCode Json.NET - это сериализатор, который ASP.NET использует по умолчанию.
Показать ещё 2 комментария
1755

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

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

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

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

  • 9
    Перейдите по ссылке для описания, как использовать его в приложении asp.net mvc james.newtonking.com/archive/2008/10/16/…
  • 2
    Вот ссылка на эту функцию: james.newtonking.com/projects/json/help/html/…
Показать ещё 11 комментариев
162

Добавьте ниже к вашему global.asax для сериализации JSON С# enum как строку

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

            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add
                (new Newtonsoft.Json.Converters.StringEnumConverter());
  • 4
    По какой-то причине я не получаю это на работу. Fiddler показывает упрямый 2, а не «Предупреждение», даже с этим на месте. Кроме того - есть ли причина, почему нужно изменить Formatting на Indented ?
  • 5
    Третья строка из этого примера была добавлена в файл App_start / webapiconfig.cs и сделала для меня хитрость в проекте ASP.NET Web API 2.1, чтобы вернуть строки для значений перечисления в вызовах REST (json fomat).
Показать ещё 4 комментария
114

@Игровый набор ответов JSON-сериализация С# enum как строка только для ASP.NET(веб-API и т.д.).

Но чтобы он работал и с сериализацией ad hoc, добавьте следующее к вашему стартовому классу (например, Global.asax 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

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

System.Runtime.Serialization.EnumMember

например:

public enum time_zone_enum
{
    [EnumMember(Value = "Europe/London")] 
    EuropeLondon,

    [EnumMember(Value = "US/Alaska")] 
    USAlaska
}
  • 1
    Спасибо! Я просто искал [EnumMember] .
31

Мне не удалось изменить исходную модель, как в верхнем ответе (@ob.), и я не хотел регистрировать ее по всему миру, как @Iggy. Поэтому я объединил https://stackoverflow.com/questions/2441290/net-json-serialization-of-enum-as-string и @Iggy https://stackoverflow.com/questions/2441290/net-json-serialization-of-enum-as-string, чтобы разрешить настройку конвертера перечислений строк во время сама команда 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()
        }
    })
  • 0
    это также хорошо, если у вас есть свойство, подобное списку <someEnumType>
30

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

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

    string GenderString { get { return Gender.ToString(); } }
}
  • 26
    Позвольте мне попытаться объяснить. Это решение не является правильным в соответствии с дизайном патерс. Вы изменили модель в соответствии с целью просмотра. Но модель должна содержать только данные и не заботится о презентациях. Вы должны переместить эту функциональность на другой слой.
  • 4
    На самом деле, модель используется для передачи данных от контроллера, и это контроллер, который не заботится о представлении. Введение автоматического свойства (здесь GenderString) не нарушает контроллер, который все еще использует свойство Gender, но обеспечивает легкий доступ для представления. Логическое решение.
Показать ещё 12 комментариев
27

Эта версия Stephen answer не изменяет имя в 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; 
        }
    }
}
  • 3
    Я считаю, что это действительно для DataContractJsonSerializer не JavaScriptSerializer
  • 0
    Просто и решает проблему для меня, используя родные .NET Framework сериализаторы.
Показать ещё 3 комментария
21

Вот ответ для newtonsoft.json

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}
  • 0
    Спасибо за этот ответ, мне очень помогли! Если вы хотите определить свои перечисления в PascalCase, но хотите, чтобы они были сериализованы в camelCase, тогда вам нужно добавить true к вашему типу JsonConverter следующим образом: [JsonConverter(typeof(StringEnumConverter), true)]
18

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

Итак, если кто-то заинтересован, это примерно так:

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

class Person
{
    int Age { get; set; }
    [JsonConverter(typeof(StringEnumConverter))]
    Gender Gender { get; set; }
}
  • 1
    Я использовал JsonPropertyAttribute для членов enum, и он работает для простых задач десериализации. К сожалению, во время ручной настройки с помощью JToken он игнорируется. Happilly EnumMemberAttribute работает как шарм. Спасибо!
13

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

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

Здесь:

Пример перечисления

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

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

Серверная сторона - С#

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. Он использует вспомогательный метод Nancy Response.AsJson() - но не беспокойтесь, вы можете использовать любой стандартный форматировщик 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>
11

Вы также можете добавить конвертер к вашему JsonSerializer, если вы не хотите использовать атрибут JsonConverter:

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

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

11

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

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

Основной способ 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

9

Для .Net Core Web Api: -

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter()));
    ...
}
  • 1
    Если это пакет NuGet из пакета Microsoft.AspNetCore.Mvc.Formatters.Json , то он представляется только методом расширения для IMvcCoreBuilder , а не IMvcBuilder . Поэтому он используется как services.AddMvcCore().AddJsonFormatters(f => f.Converters.Add(new StringEnumConverter())); ,
7

Отмечено, что нет ответа на сериализацию при наличии атрибута 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; }
5

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

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

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

  • 0
    Приятно узнать, что особенность Automapper ;-) [ScriptIgnore] атрибут удалит циклические ссылки
  • 1
    Ой. Не знал об атрибуте. Спасибо! Вы бы использовали это на вашем Pocos? Я прибегнул к использованию определений MetadataType для любых атрибутов Poco, чтобы сохранить их чистоту. Будет ли атрибут работать через метаданные?
4

Для ядра ASP.Net Просто добавьте следующее в свой класс запуска:

JsonConvert.DefaultSettings = (() =>
        {
            var settings = new JsonSerializerSettings();
            settings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
            return settings;
        });
4

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

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

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

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

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

  • 0
    Очень интересное решение! Спасибо, что поделился.
0

Не уверен, что это все еще актуально, но мне пришлось писать прямо в файл json, и я придумал следующий набор из нескольких ответов stackoverflow

открытый класс LowercaseJsonSerializer {частная статическая только для чтения JsonSerializerSettings Settings = new JsonSerializerSettings {ContractResolver = new LowercaseContractResolver()};

public static void Serialize(TextWriter file, object o)
{
    JsonSerializer serializer = new JsonSerializer()
    {
        ContractResolver = new LowercaseContractResolver(),
        Formatting = Formatting.Indented,
        NullValueHandling = NullValueHandling.Ignore
    };
    serializer.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
    serializer.Serialize(file, o);
}

public class LowercaseContractResolver : DefaultContractResolver
{
    protected override string ResolvePropertyName(string propertyName)
    {
        return Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1);
    }
}

}

Это гарантирует, что все мои ключи JSON начинаются со строчной буквы в соответствии с "правилами" JSON. Форматирует его с чистым отступом и игнорирует нули в выходных данных. Также, добавив StringEnumConverter, он печатает перечисления с их строковым значением.

Лично я считаю, что это самое чистое, что я мог придумать, не пачкая модель аннотациями.

0

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

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

-3
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]  
);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню