it-swarm.com.ru

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

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

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

enum Gender { Male, Female }

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

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

{ "Age": 35, "Gender": "Male" }
950
Omer Bokhari

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

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

271
Matt Dearing

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

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

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

Более подробную информацию можно найти в StringEnumConverter документации .

1805
Omer Bokhari

Добавьте ниже в ваш global.asax для JSON сериализации c # 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());
158
Iggy

Ответ @Iggy устанавливает JSON-сериализацию c # enum в виде строки только для ASP.NET (веб-API и т.д.).

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

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

System.Runtime.Serialization.EnumMember

атрибут, как это:

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

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

Я не смог изменить исходную модель, как в верхнем ответе (@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()
        }
    })
31
Scott Stafford

Это легко сделать, добавив атрибут ScriptIgnore к свойству Gender, сделав его не сериализованным, и добавив свойство GenderString, которое does получает сериализацию:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

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

Эта версия Стивена 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; 
        }
    }
}
26
mheyman

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

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

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

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

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

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

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

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

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

Вот оно:

Пример Enum

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

Сложное перечисление, которое использует побитовые ИЛИ для генерации системы разрешений. Таким образом, вы не можете полагаться на простой индекс [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. Он использует вспомогательный метод Нэнси 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>
13
biofractal

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

12
st1

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

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

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

11
JerryGoyal

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

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

Для .Net Core Web Api: -

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

Заметил, что при сериализации нет ответа для сериализации.

Вот моя реализация, которая поддерживает атрибут 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");
    }
}

Enum: 

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; }
7
Greg R Taylor

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

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

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

5
Ales Potocnik Hahonina

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

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

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

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

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

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

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

3
Sebastian Markbåge

А для VB.net я нашел следующие работы:

Dim sec = New Newtonsoft.Json.Converters.StringEnumConverter()
sec.NamingStrategy() = New Serialization.CamelCaseNamingStrategy

Dim JSON_s As New JsonSerializer
JSON_s.Converters.Add(sec)

Dim jsonObject As JObject
jsonObject = JObject.FromObject(SomeObject, JSON_s)
Dim text = jsonObject.ToString

IO.File.WriteAllText(filePath, text)
0
Benjamin Swedlove

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

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

0
Jon Grant

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

public class LowercaseJsonSerializer
{
    private static readonly 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, он печатает перечисления с их строковым значением.

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

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

    internal void SaveJson(string fileName)
    {
        // serialize JSON directly to a file
        using (StreamWriter file = File.CreateText(@fileName))
        {
            LowercaseJsonSerializer.Serialize(file, jsonobject);
        }
    }
0
kenny