it-swarm.it

.NET - Serializzazione JSON di enum come stringa

Ho una classe che contiene una proprietà enum, e sulla serializzazione dell'oggetto usando JavaScriptSerializer, il mio risultato json contiene il valore intero dell'enumerazione piuttosto che il suo string "nome". C'è un modo per ottenere l'enum come string nel mio json senza dover creare una custom JavaScriptConverter? Forse c'è un attributo che potrei decorare con la definizione enum o con la proprietà dell'oggetto?

Come esempio:

enum Gender { Male, Female }

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

Risultato json desiderato:

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

No non ci sono attributi speciali che puoi usare. JavaScriptSerializer serializza enums ai loro valori numerici e non alla loro rappresentazione di stringa. È necessario utilizzare la serializzazione personalizzata per serializzare enum come nome anziché come valore numerico.

Modifica: Come sottolineato da @OmerBakhari JSON.net tratta questo caso d'uso (tramite l'attributo [JsonConverter(typeof(StringEnumConverter))]) e molti altri non gestiti dai serializzatori .net incorporati. Ecco un link che confronta caratteristiche e funzionalità dei serializzatori .

271
Matt Dearing

Ho trovato che Json.NET fornisce la funzionalità esatta che sto cercando con un attributo StringEnumConverter:

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

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

Maggiori dettagli sono disponibili su StringEnumConverter documentation .

1805
Omer Bokhari

Aggiungi il sotto al tuo global.asax per la serializzazione JSON di c # enum come stringa

  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 answer imposta la serializzazione JSON di c # enum come stringa solo per ASP.NET (Web API e così via).

Ma per farlo funzionare anche con la serializzazione ad hoc, aggiungi quanto segue alla tua classe di partenza (come 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;
});

Ulteriori informazioni sulla pagina Json.NET

Inoltre, per far serializzare/deserializzare il tuo membro enum in/da uno specifico testo, usa il comando 

System.Runtime.Serialization.EnumMember

attributo, come questo:

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

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

Non ero in grado di modificare il modello sorgente come nella risposta in alto (di @ob.), E non volevo registrarlo globalmente come @Iggy. Così ho combinato https://stackoverflow.com/a/2870420/237091 e @ Iggy's https://stackoverflow.com/a/18152942/237091 per consentire la configurazione del convertitore enum di stringhe durante il comando SerializeObject stesso:

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

Questo è fatto facilmente aggiungendo un attributo ScriptIgnore alla proprietà Gender, causando che non venga serializzato e aggiungendo una proprietà GenderString che fa ottiene serializzato:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

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

Questa versione di Stephen's answer non modifica il nome nel 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

La combinazione delle risposte di Omer Bokhari e di uri è sempre la mia soluzione dato che i valori che voglio fornire sono solitamente diversi da quelli che ho nel mio enum specialmente che mi piacerebbe poter cambiare le mie enumerazioni se necessario.

Quindi se qualcuno è interessato, è qualcosa del genere:

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

Ecco la risposta per newtonsoft.json

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

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

Ecco una soluzione semplice che serializza un enum di C # dal lato server a JSON e utilizza il risultato per popolare un elemento <select> sul lato client. Funziona sia per enumerazioni semplici sia per enfasi bitflag.

Ho incluso la soluzione end-to-end perché penso che la maggior parte delle persone che desiderano serializzare un enum di C # in JSON lo utilizzeranno probabilmente anche per riempire un menu a discesa <select>

Ecco qui:

Esempio Enum

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

Un enum complesso che utilizza OR bit a bit per generare un sistema di autorizzazioni. Quindi non puoi fare affidamento sull'indice semplice [0,1,2 ..] per il valore intero dell'enum.

Lato server - 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);
};

Il codice sopra utilizza il framework NancyFX per gestire la richiesta Get. Utilizza il metodo helper Response.AsJson() di Nancy, ma non preoccuparti, puoi utilizzare qualsiasi formattatore JSON standard poiché l'enum è già stato proiettato in un tipo anonimo semplice pronto per la serializzazione.

JSON generato

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

Lato client - 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 prima

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

HTML After

<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

Modo base 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

Puoi anche aggiungere un convertitore al tuo JsonSerializer se non vuoi usare l'attributo JsonConverter:

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

Funzionerà per ogni enum che vede durante quella serializzazione.

11
JerryGoyal

È possibile creare JsonSerializerSettings con la chiamata a JsonConverter.SerializeObject come di seguito:

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

Per .Net Core Web Api: -

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

Notato che non esiste una risposta per la serializzazione quando esiste un attributo Descrizione.

Ecco la mia implementazione che supporta l'attributo 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
}

Uso:

[JsonConverter(typeof(CustomStringEnumConverter))]
public FooEnum test { get; set; }
7
Greg R Taylor

Questa è una vecchia domanda, ma ho pensato di contribuire nel caso. Nei miei progetti uso modelli separati per qualsiasi richiesta Json. In genere un modello ha lo stesso nome dell'oggetto dominio con il prefisso "Json". I modelli sono mappati usando AutoMapper . Avendo il modello JSON dichiarare una proprietà stringa che è un enum su una classe di dominio, AutoMapper risolverà la sua presentazione di stringa.

Nel caso ve lo stiate chiedendo, ho bisogno di modelli separati per le classi serializzate Json, perché il serializzatore integrato fornisce altrimenti riferimenti circolari.

Spero che questo aiuti qualcuno.

Per nucleo ASP.Net Basta aggiungere quanto segue alla tua classe di avvio:

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

Nel caso in cui qualcuno trovi il precedente insufficiente, ho finito per sistemare questo sovraccarico:

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

In realtà, puoi utilizzare JavaScriptConverter per realizzare questo con JavaScriptSerializer incorporato. Convertendo il tuo enum in un Uri puoi codificarlo come una stringa.

Ho descritto come farlo per le date, ma può essere usato anche per le enumerazioni.

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

3

E per VB.net ho trovato i seguenti lavori:

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

Ho assemblato tutti i pezzi di questa soluzione usando la libreria Newtonsoft.Json. Corregge il problema enum e rende la gestione degli errori molto migliore, e funziona in IIS servizi ospitati. È un bel po 'di codice, quindi puoi trovarlo su GitHub qui: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

Devi aggiungere alcune voci al tuo Web.config per farlo funzionare, puoi vedere un file di esempio qui: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

0
Jon Grant

Non sono sicuro se questo è ancora rilevante, ma ho dovuto scrivere direttamente su un file JSON e ho trovato il seguente riepilogo di diverse risposte StackOverflow insieme

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);
        }
    }
}

Assicura che tutte le mie chiavi JSON siano in minuscolo a partire da "regole" json. Formatta con un rientro pulito e ignora i valori nulli nell'output. Inoltre, aggiungendo StringEnumConverter, stampa enumerazioni con il loro valore di stringa.

Personalmente trovo questo il più pulito che potrei inventare, senza dover sporcare il modello con annotazioni.

utilizzo:

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