web-dev-qa-db-ja.com

.NET - 文字列としてのenumのJSONシリアライゼーション

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" }
992
Omer Bokhari

いいえ使用できる特別な属性はありません。 JavaScriptSerializerは、文字列表現ではなく数値にenumsをシリアル化します。 enumを数値ではなく名前としてシリアル化するには、カスタムシリアル化を使用する必要があります。

編集: @OmerBakhariが指摘しているように、JSON.netは(ユースケースの属性[JsonConverter(typeof(StringEnumConverter))]を介して)このユースケースをカバーしています。 これはシリアライザの機能を比較するリンクです

298
Matt Dearing

私は Json.NETStringEnumConverter属性で私が探している正確な機能を提供することを発見しました:

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

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

詳細は StringEnumConverterのドキュメント にあります。

1862
Omer Bokhari

文字列としてc#enumのJSONシリアライゼーションのために以下をglobal.asaxに追加してください

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

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

@Iggy answerはc#enumのJSONシリアライゼーションをASP.NET専用の文字列として設定します(Web 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
}
126
Juri

私は(@ ob。の)一番上の答えのようにソースモデルを変更することができませんでした、そして私は@Iggyのようにグローバルにそれを登録したくありませんでした。そこで、 https://stackoverflow.com/a/2870420/237091 と@ Iggyの 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()
        }
    })
34
Scott Stafford

これは、 ScriptIgnore 属性をGenderプロパティに追加して、それをシリアル化しないようにし、does _シリアル化されるGenderStringプロパティを追加することで簡単に行えます。

class Person
{
    int Age { get; set; }

    [ScriptIgnore]
    Gender Gender { get; set; }

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

Omer Bokhariとuriの答えを組み合わせることは、私の解決策でもあります。私が提供したい値は、通常必要に応じて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; }
}
27
Ashkan Sirous

このバージョンの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; 
        }
    }
}
26
mheyman

これがnewtonsoft.jsonの答えです。

enum Gender { Male, Female }

class Person
{
    int Age { get; set; }

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

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

15
st1

これは、サーバーサイドのC#enumをJSONにシリアル化し、その結果を使用してクライアントサイドの<select>要素を設定する簡単な解決策です。これは単純なenumとbitflagのenumの両方に有効です。

私はC#enumをJSONにシリアライズしたいと思うほとんどの人がおそらく<select>ドロップダウンを埋めるためにそれを使っていると思うので、私はエンドツーエンドのソリューションを含めました。

これが行きます:

例の列挙

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

パーミッションシステムを生成するためにビットごとのORを使用する複雑な列挙体。そのため、enumの整数値に単純なインデックス[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);
};

上記のコードは、Getリクエストを処理するためにNancyFXフレームワークを使用しています。これは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>
14
biofractal

JsonSerializer属性を使用したくない場合は、JsonConverterにコンバーターを追加することもできます。

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

それはそのシリアライゼーションの間に見るすべてのenumに対して働きます。

13
JerryGoyal

以下のようにJsonConverter.SerializeObjectを呼び出してJsonSerializerSettingsを作成できます。

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

ASP.Netコアの場合スタートアップクラスに以下を追加するだけです。

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

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

これは古い質問ですが、念のために貢献したいと思いました。私のプロジェクトでは、Jsonの要求に応じて別々のモデルを使用しています。モデルは通常、 "Json"プレフィックスを持つドメインオブジェクトと同じ名前を持ちます。モデルは AutoMapper を使用してマッピングされます。 jsonモデルにドメインクラスの列挙型の文字列プロパティを宣言させることで、AutoMapperはその文字列表現を解決します。

ご参考までに、Jsonのシリアライズクラスには別のモデルが必要です。組み込みのシリアライザには循環参照があります。

これが誰かに役立つことを願っています。

万が一誰かが上記の不十分さを発見した場合、私はこの過負荷で落ち着いた。

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

JavaScriptConverterを実際に使用して、組み込みのJavaScriptSerializerを使用してこれを実現できます。あなたのenumをUriに変換することによってそれを文字列としてエンコードすることができます。

日付に対してこれを行う方法を説明しましたが、enumにも使用できます。

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

3

これがまだ関連があるかどうかわからないが、私は直接JSONファイルに書かなければならなかったと私は一緒にいくつかのスタックオーバーフローの答えをつなぐことを思い付いた

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 "rules"に従って小文字で始まることを保証します。きれいにインデントされた形式にフォーマットし、出力のnullを無視します。 StringEnumConverterを追加することで、文字列値を含む列挙型を出力します。

個人的には、モデルをアノテーションで汚す必要なしに、私が思いつくことができる最もきれいな方法だと思います。

使用法:

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

そして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

少し未来のオプション

同じ質問に直面して、列挙値がデシリアライズ側で壊滅的に破損することなく時間とともに拡大することを確認するために、StringEnumConverterのカスタムバージョンが必要であると判断しました(以下の背景を参照)。以下のSafeEnumConverterを使用すると、ペイロードに名前付き定義のない列挙型の値が含まれている場合でも、intからenumへの変換がどのように機能するかに近いように、逆シリアル化を終了できます。

使用法:

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

または

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

ソース:

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

バックグラウンド

StringEnumConverterの使用を見たときの問題は、新しい列挙値が追加されたが、すべてのクライアントがすぐに新しい値を認識したわけではない場合の受動性も必要だったことです。これらの場合、Newtonsoft JSONでパッケージ化されたStringEnumConverterは、「値SomeStringをEnumType型に変換するエラー」に類似したJsonSerializationExceptionをスローし、whole逆シリアル化プロセスは失敗します。クライアントが理解していないプロパティ値を無視/破棄することを計画していたとしても、残りのペイロードをデシリアライズできる必要があるため、これは私たちにとっては契約違反となりました。

0
Dusty

このソリューションのすべての部分を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