it-swarm.com.de

.NET - JSON-Serialisierung von Enum als String

Ich habe eine Klasse, die eine enum -Eigenschaft enthält. Bei der Serialisierung des Objekts mit JavaScriptSerializer enthält mein json-Ergebnis den ganzzahligen Wert der Aufzählung und nicht dessen string "name". Gibt es eine Möglichkeit, das Enum als string in meinem Json abzurufen, ohne eine benutzerdefinierte JavaScriptConverter erstellen zu müssen? Vielleicht gibt es ein Attribut, mit dem ich die enum-Definition oder die Objekteigenschaft mit dekorieren könnte?

Als Beispiel:

enum Gender { Male, Female }

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

Gewünschtes Json-Ergebnis:

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

Nein, es gibt kein spezielles Attribut, das Sie verwenden können. JavaScriptSerializer serialisiert enums zu ihren numerischen Werten und nicht zu ihrer Zeichenfolgendarstellung. Sie müssten eine benutzerdefinierte Serialisierung verwenden, um die enum anstelle des numerischen Werts als Namen zu serialisieren.

Edit: Wie von @OmerBakhari ausgeführt, behandelt JSON.net diesen Anwendungsfall (über das Attribut [JsonConverter(typeof(StringEnumConverter))]) und viele andere, die nicht von den integrierten .net-Serialisierern behandelt werden. Hier ist ein Link zum Vergleichen von Funktionen und Funktionalitäten der Serialisierer .

271
Matt Dearing

Ich habe festgestellt, dass Json.NET die genaue Funktionalität bietet, die ich mit einem StringEnumConverter-Attribut suche:

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

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

Weitere Einzelheiten finden Sie unter StringEnumConverter Dokumentation .

1805
Omer Bokhari

Fügen Sie Ihrer global.asax für die JSON-Serialisierung von c # enum als String das Folgende hinzu

  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

Die @ Iggy-Antwort setzt die JSON-Serialisierung von c # enum als Zeichenfolge nur für ASP.NET (Web-API usw.).

Damit es auch mit Ad-hoc-Serialisierung funktioniert, fügen Sie Ihrer Startklasse Folgendes hinzu (wie 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;
});

Weitere Informationen auf der Json.NET-Seite

Damit Ihr Enum-Member zu einem bestimmten Text serialisiert/deserialisiert wird, verwenden Sie außerdem die 

System.Runtime.Serialization.EnumMember

attribut wie folgt:

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

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

Ich konnte das Quellmodell nicht wie in der obersten Antwort (von @ob.) Ändern und wollte es nicht global wie @Iggy registrieren. Also kombinierte ich https://stackoverflow.com/a/2870420/237091 und @ Iggy's https://stackoverflow.com/a/18152942/237091 , um die Einrichtung des String-Enum-Konverters während zu ermöglichen Der SerializeObject-Befehl selbst:

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

Dies kann leicht durch Hinzufügen eines ScriptIgnore -Attributs zur Gender -Eigenschaft erreicht werden, wodurch es nicht serialisiert wird, und eine GenderString -Eigenschaft hinzugefügt, die do serialisiert wird:

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

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

Diese Version von Stephens answer ändert den Namen in der JSON nicht:

[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

Die Kombination der Antworten von Omer Bokhari und uri ist auch immer meine Lösung, da sich die Werte, die ich liefern möchte, normalerweise von denen unterscheiden, die ich in meinem Enum habe, insbesondere, dass ich mein Enum ändern möchte, wenn ich es brauche.

Wenn also jemand interessiert ist, ist es so ähnlich:

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

Hier ist die Antwort für newtonsoft.json

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

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

Hier ist eine einfache Lösung, die ein serverseitiges C # -Enum in JSON serialisiert und das Ergebnis dazu verwendet, ein clientseitiges <select>-Element aufzufüllen. Dies funktioniert sowohl für einfache als auch für bitflag-Enums.

Ich habe die End-to-End-Lösung hinzugefügt, weil ich glaube, dass die meisten Leute, die ein C # -Enum zu JSON serialisieren wollen, es wahrscheinlich auch zum Füllen eines <select>-Dropdown-Menüs verwenden werden. 

Hier geht:

Beispiel Enum

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

Eine komplexe Enumeration, die bitweise ORs zum Generieren eines Berechtigungssystems verwendet. Sie können sich also nicht auf den einfachen Index [0,1,2 ..] für den ganzzahligen Wert der Aufzählung verlassen.

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

Der obige Code verwendet das NancyFX-Framework, um die Get-Anforderung zu verarbeiten. Es verwendet die Response.AsJson() Helper-Methode von Nancy - aber keine Sorge, Sie können jeden Standard-JSON-Formatierer verwenden, da das Enum bereits in einen einfachen anonymen Typ projiziert wurde, der für die Serialisierung bereit ist.

Generierte JSON

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

Clientseite - 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 vor

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

HTML nach

<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 Core-Methode:

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/27f62ef83a804668eb0d9d0f63989e3d

12
st1

Sie können auch einen Konverter zu Ihrem JsonSerializer hinzufügen, wenn Sie das JsonConverter-Attribut nicht verwenden möchten:

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

Es funktioniert für jedes enum, das es während dieser Serialisierung sieht.

11
JerryGoyal

Sie können JsonSerializerSettings mit dem Aufruf von JsonConverter.SerializeObject wie folgt erstellen:

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

Für .Net Core Web Api: -

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

Beachten Sie, dass es keine Antwort für die Serialisierung gibt, wenn ein Description-Attribut vorhanden ist.

Hier ist meine Implementierung, die das Attribut Description unterstützt.

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

Aufzählung: 

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

    // Will be serialized as "Applicable"
    Applicable
}

Verwendungszweck:

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

Dies ist eine alte Frage, aber ich dachte, ich würde nur für den Fall beitragen. In meinen Projekten verwende ich separate Modelle für Json-Anfragen. Ein Modell hätte normalerweise denselben Namen wie ein Domänenobjekt mit dem Präfix "Json". Modelle werden mit AutoMapper zugeordnet. Wenn das json-Modell eine Zeichenfolgeneigenschaft deklariert, die eine Aufzählung für die Domänenklasse darstellt, wird AutoMapper in seine Zeichenfolgendarstellung aufgelöst.

Falls Sie sich fragen, benötige ich separate Modelle für serialisierte Json-Klassen, da der eingebaute Serializer ansonsten mit Zirkelreferenzen aufwartet.

Hoffe das hilft jemandem.

Für den ASP.Net-Kern fügen Sie Ihrer Startup-Klasse einfach Folgendes hinzu:

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

Nur für den Fall, dass irgendjemand dies als unzureichend erachtet, entschied ich mich für diese Überlastung:

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

Sie können tatsächlich einen JavaScriptConverter verwenden, um dies mit dem integrierten JavaScriptSerializer zu erreichen. Wenn Sie Ihr Enum in einen Uri konvertieren, können Sie es als String kodieren.

Ich habe beschrieben, wie dies für Datumsangaben gemacht wird, aber es kann auch für Aufzählungen verwendet werden.

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

3

Und für VB.net habe ich folgende Werke gefunden:

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

Eine etwas zukunftssicherere Option

Angesichts der gleichen Frage haben wir festgestellt, dass wir eine benutzerdefinierte Version von StringEnumConverter benötigen, um sicherzustellen, dass unsere Enum-Werte mit der Zeit erweitert werden können, ohne katastrophal auf der deserialisierenden Seite zu brechen (siehe Hintergrund unten). Durch die Verwendung von SafeEnumConverter kann die Deserialisierung abgeschlossen werden, auch wenn die Nutzdaten einen Wert für die Aufzählung enthalten, für die keine benannte Definition vorhanden ist, näher an der Funktionsweise der Konvertierung von Int zu Enum.

Verwendungszweck:

[SafeEnumConverter]
public enum Colors
{
    Red,
    Green,
    Blue,
    Unsupported = -1
}

oder

[SafeEnumConverter((int) Colors.Blue)]
public enum Colors
{
    Red,
    Green,
    Blue
}

Quelle:

public class SafeEnumConverter : StringEnumConverter
{
    private readonly int _defaultValue;

    public SafeEnumConverter()
    {
        // if you've been careful to *always* create enums with `0` reserved
        // as an unknown/default value (which you should), you could use 0 here. 
        _defaultValue = -1;
    }

    public SafeEnumConverter(int defaultValue)
    {
        _defaultValue = defaultValue;
    }

    /// <summary>
    /// Reads the provided JSON and attempts to convert using StringEnumConverter. If that fails set the value to the default value.
    /// </summary>
    /// <returns>The deserialized value of the enum if it exists or the default value if it does not.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return base.ReadJson(reader, objectType, existingValue, serializer);
        }
        catch
        {
            return Enum.Parse(objectType, $"{_defaultValue}");
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return base.CanConvert(objectType) && objectType.GetTypeInfo().IsEnum;
    }
}

Hintergrund

Als wir uns die Verwendung von StringEnumConverter angesehen haben, bestand das Problem darin, dass wir auch Passivität für Fälle benötigten, in denen ein neuer Aufzählungswert hinzugefügt wurde, aber nicht jeder Kunde den neuen Wert sofort bemerkte. In diesen Fällen gibt das mit Newtonsoft JSON gepackte StringEnumConverter ein JsonSerializationException aus, das "Fehler beim Konvertieren des Werts SomeString in Typ EnumType" ähnelt, und der vollständige Deserialisierungsprozess schlägt fehl. Dies war ein Deal Breaker für uns, denn selbst wenn der Kunde vorhatte, den nicht verstandenen Immobilienwert zu ignorieren/zu verwerfen, musste er in der Lage sein, den Rest der Nutzlast zu deserialisieren!

0
Dusty

Ich habe alle Teile dieser Lösung mit der Newtonsoft.Json-Bibliothek zusammengestellt. Es behebt das Problem mit der Enumeration und verbessert die Fehlerbehandlung erheblich. Es funktioniert in IIS gehosteten Services. Es ist ziemlich viel Code, also kannst du ihn auf GitHub hier finden: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

Sie müssen Ihrem Web.config einige Einträge hinzufügen, damit er funktionieren kann. Eine Beispieldatei finden Sie hier: https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

0
Jon Grant

Ich bin nicht sicher, ob dies noch relevant ist, aber ich musste direkt in eine Json-Datei schreiben, und ich stellte mir die folgenden Antworten zusammen

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

Es stellt sicher, dass alle meine Json-Schlüssel nach den Json-Regeln klein geschrieben werden. Formatiert es sauber eingerückt und ignoriert Nullen in der Ausgabe. Durch das Hinzufügen eines StringEnumConverter werden auch Enumerationen mit ihrem String-Wert gedruckt.

Ich persönlich finde das am saubersten, was ich finden konnte, ohne das Modell mit Anmerkungen beschmutzen zu müssen.

verwendungszweck:

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