web-dev-qa-db-ja.com

列挙型を文字列にシリアル化します

私は列挙型を持っています:

public enum Action {
    Remove=1,
    Add=2
}

そしてクラス:

[DataContract]
public class Container {
    [DataMember]
    public Action Action {get; set;}
}

Containerのインスタンスをjsonにシリアル化すると、次のようになります:{Action:1}(アクションが削除の場合)。

取得したい:{Action:Remove}(intの代わりに、列挙型のToString形式が必要です)

クラスに別のメンバーを追加せずにできますか?

46
Naor

JSONフォーマッタは、列挙を操作するときに非常に特殊な動作をします。通常のデータコントラクト属性は無視され、他の形式で予想されるより人間が読める文字列ではなく、enumを数値として扱います。これにより、フラグ型の列挙を簡単に処理できるようになりますが、他のほとんどの型の処理ははるかに困難になります。

[〜#〜] msdn [〜#〜] から:

列挙メンバーの値は、JSONで数値として扱われます。これは、メンバー名として含まれるデータコントラクトでの処理方法とは異なります。データコントラクトの処理の詳細については、「 データコントラクトの列挙型 」を参照してください。

  • たとえば、public enum Color {red, green, blue, yellow, pink}、黄色をシリアル化すると、文字列「yellow」ではなく、番号3が生成されます。

  • すべての列挙型メンバーはシリアル化可能です。 EnumMemberAttributeおよびNonSerializedAttribute属性は、使用されても無視されます。

  • 存在しない列挙値を逆シリアル化することは可能です。たとえば、対応する色名が定義されていなくても、値87は前のColor列挙に逆シリアル化できます。

  • Flags列挙は特別ではなく、他の列挙と同じように扱われます。

これを解決する唯一の実用的な方法は、エンドユーザーが数値ではなく文字列を指定できるようにするために、契約で列挙型を使用しないことです。代わりに、実際の答えは、enumを文字列で置き換え、値を有効なenum表現の1つに解析できるように値の内部検証を実行することです。

代わりに(心臓の弱者向けではありませんが)、JSONフォーマッターを独自のものに置き換えることができます。これにより、他のフォーマッターと同じ方法で列挙が尊重されます。

21
Paul Turner

Json.Net を使用して、カスタムStringEnumConverterを次のように定義できます。

public class MyStringEnumConverter : Newtonsoft.Json.Converters.StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value is Action)
        {
            writer.WriteValue(Enum.GetName(typeof(Action),(Action)value));// or something else
            return;
        }

        base.WriteJson(writer, value, serializer);
    }
}

としてシリアライズ

string json=JsonConvert.SerializeObject(container,new MyStringEnumConverter());
30
L.B

属性を追加するだけです:

    [Newtonsoft.Json.Converters.JsonConverter(typeof(StringEnumConverter))] 

文字列としてシリアル化されていない列挙型プロパティ。

または、よりエキゾチックなフォーマットを念頭に置いている場合は、以下の属性を使用して、JSONシリアライザーに、希望どおりにフォーマットしたプロパティのみをシリアル化するように指示できます。実装の残りの部分に少し依存します。プロパティのDataMember属性も認識します。

[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class Container
{
    public Action Action { get; set; }

    [JsonProperty(PropertyName = "Action")]
    public string ActionString
    {
        get
        {
            return Action.ToString();
        }
    }
}
19
GPR

これを行う簡単な方法を次に示します。

JsonConvert.SerializeObject(myObject, Formatting.Indented, new StringEnumConverter());
12
ShaTin

列挙型メンバー名またはEnumMemberAttributeの値によるシリアル化のいずれかで機能するシリアル化および逆シリアル化に補助プライベートプロパティを使用することにより、非常に優れた回避策を使用しています。

私が見る最大の利点は次のとおりです。

  • シリアライザーで微調整する必要はありません
  • すべてのシリアル化ロジックはデータオブジェクトに含まれています
  • DataContractSerializersはプライベートプロパティを取得および設定できるため、補助プロパティのアクセシビリティ修飾子をプライベートに設定することで、補助プロパティを非表示にできます。
  • 列挙型をstringではなくintとしてシリアル化できます。

クラスは次のようになります。

[DataContract]
public class SerializableClass {
    public Shapes Shape {get; set;} //Do not use the DataMemberAttribute in the public property

    [DataMember(Name = "shape")]
    private string ShapeSerialization // Notice the PRIVATE here!
    {
        get { return EnumHelper.Serialize(this.Shape); }
        set { this.Shape = EnumHelper.Deserialize<Shapes>(value); }
    }
}

EnumHelper.cs

/* Available at: https://Gist.github.com/mniak/a4d09264ad1ca40c489178325b98935b */
public static class EnumHelper
{
    public static string Serialize<TEnum>(TEnum value)
    {
        var fallback = Enum.GetName(typeof(TEnum), value);
        var member = typeof(TEnum).GetMember(value.ToString()).FirstOrDefault();
        if (member == null)
            return fallback;
        var enumMemberAttributes = member.GetCustomAttributes(typeof(EnumMemberAttribute), false).Cast<EnumMemberAttribute>().FirstOrDefault();
        if (enumMemberAttributes == null)
            return fallback;
        return enumMemberAttributes.Value;
    }
    public static TEnum Deserialize<TEnum>(string value) where TEnum : struct
    {
        TEnum parsed;
        if (Enum.TryParse<TEnum>(value, out parsed))
            return parsed;

        var found = typeof(TEnum).GetMembers()
            .Select(x => new
            {
                Member = x,
                Attribute = x.GetCustomAttributes(typeof(EnumMemberAttribute), false).OfType<EnumMemberAttribute>().FirstOrDefault()
            })
            .FirstOrDefault(x => x.Attribute?.Value == value);
        if (found != null)
            return (TEnum)Enum.Parse(typeof(TEnum), found.Member.Name);
        return default(TEnum);
    }
}
3
Andre Soares

Michal Bによって投稿されたソリューションはうまく機能します。別の例を示します。

Description属性はシリアル化できないため、以下を実行する必要があります。

[DataContract]
public enum ControlSelectionType
{
    [EnumMember(Value = "Not Applicable")]
    NotApplicable = 1,
    [EnumMember(Value = "Single Select Radio Buttons")]
    SingleSelectRadioButtons = 2,
    [EnumMember(Value = "Completely Different Display Text")]
    SingleSelectDropDownList = 3,
}


public static string GetDescriptionFromEnumValue(Enum value)
{
    EnumMemberAttribute attribute = value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(typeof(EnumMemberAttribute), false)
        .SingleOrDefault() as EnumMemberAttribute;
    return attribute == null ? value.ToString() : attribute.Value;}
1
Theo Koekemoer

シリアル化の目的で、コンテナに列挙プロパティを含めることはできませんが、入力する場合は、以下の拡張メソッドを使用できます。

コンテナ定義

public class Container
{
    public string Action { get; set; }
}

列挙定義

public enum Action {
    Remove=1,
    Add=2
}

ビュー内のコード

@Html.DropDownListFor(model => model.Action, typeof (Action))

拡張方法

/// <summary>
/// Returns an HTML select element for each property in the object that is represented by the specified expression using the given enumeration list items.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <typeparam name="TProperty">The type of the value.</typeparam>
/// <param name="htmlHelper">The HTML helper instance that this method extends.</param>
/// <param name="expression">An expression that identifies the object that contains the properties to display.</param>
/// <param name="enumType">The type of the enum that fills the drop box list.</param>
/// <returns>An HTML select element for each property in the object that is represented by the expression.</returns>
public static MvcHtmlString DropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression, Type enumType)
{
    var values = from Enum e in Enum.GetValues(enumType)
                    select new { Id = e, Name = e.ToString() };

    return htmlHelper.DropDownListFor(expression, new SelectList(values, "Id", "Name"));
}
0
ZenLulz

使用してみてください

public enum Action {
    [EnumMember(Value = "Remove")]
    Remove=1,
    [EnumMember(Value = "Add")]
    Add=2
}

これがあなたのケースに合うかどうかはわかりませんが、間違っているかもしれません。

ここで説明されています: http://msdn.Microsoft.com/en-us/library/aa347875.aspx

0
Michal B.

Newtonsoft.Jsonライブラリを使用して、これに対する解決策を示しました。列挙型の問題を修正し、エラー処理を大幅に改善し、自己ホスト型サービスではなくIISホスト型サービスで動作します。変更や特別な何かを追加する必要はありませんDataContractクラス:非常に多くのコードであるため、GitHubで見つけることができます: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs

Web.configにいくつかのエントリを追加して動作させる必要があります。ここにサンプルファイルがあります。 https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config

0
Jon Grant