json.netを使用して、プロパティを逆シリアル化し、シリアル化しない


122

Json.netでC#オブジェクトをシリアル化することによって生成されたいくつかの構成ファイルがあります。

シリアル化されたクラスの1つのプロパティを、単純なenumプロパティからクラスプロパティに移行したいと思います。

これを行う簡単な方法の1つは、クラスに古い列挙型プロパティを残し、Json.netが構成を読み込むときにこのプロパティを読み取るように設定し、次にオブジェクトをシリアル化するときに再度保存しないようにすることです。古い列挙型から新しいクラスを個別に生成することを扱います。

Json.netがシリアル化時にのみそれを無視し、逆シリアル化時にそれに注意を払うように、C#オブジェクトのプロパティを(たとえば属性で)マークする簡単な方法はありますか?


カスタムコンバーターについてはどうですか。プロパティの属性として使用し、ReadJsonとWriteJsonを別の割合でオーバーライドできますか?例(ない正確に何が必要、しかし...)weblogs.asp.net/thangchung/archive/2010/08/26/...
ラファエルAlthaus


`[JsonIgnore] '属性を使用してそれを行うことはできませんか?james.newtonking.com/archive/2009/10/23/...
樹里

qの最後の段落のように、これをどのように一方向にのみ使用できるかを詳しく説明できますか?
ウィルディーン

[JsonProperty]属性で装飾されたセカンダリプライベートセッターと[JsonIgnore]を組み合わせて使用​​できます。他にもいくつかの簡単な解決策があります。詳細な記事を追加しました。
ブライアンロジャース

回答:


132

希望する結果を得るために使用できるかなり単純なアプローチがいくつかあります。

たとえば、現在次のように定義されているクラスがあるとします。

class Config
{
    public Fizz ObsoleteSetting { get; set; }
    public Bang ReplacementSetting { get; set; }
}

enum Fizz { Alpha, Beta, Gamma }

class Bang
{
    public string Value { get; set; }
}

そして、あなたはこれをしたいです:

string json = @"{ ""ObsoleteSetting"" : ""Gamma"" }";

// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);

// migrate
config.ReplacementSetting = 
    new Bang { Value = config.ObsoleteSetting.ToString() };

// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);

これを取得するには:

{"ReplacementSetting":{"Value":"Gamma"}}

アプローチ1:ShouldSerializeメソッドを追加する

Json.NETには、ShouldSerializeクラス内の対応するメソッドを探すことにより、プロパティを条件付きでシリアル化する機能があります。

この機能を使用するには、ブールShouldSerializeBlah()メソッドをクラスに追加します。ここで、Blahは、シリアル化しないプロパティの名前に置き換えます。このメソッドの実装が常に返されるようにしfalseます。

class Config
{
    public Fizz ObsoleteSetting { get; set; }

    public Bang ReplacementSetting { get; set; }

    public bool ShouldSerializeObsoleteSetting()
    {
        return false;
    }
}

注:このアプローチは好きだが、ShouldSerializeメソッドを導入してクラスのパブリックインターフェースを混乱させたくない場合は、を使用しIContractResolverてプログラムで同じことを行うことができます。ドキュメントの「条件付きプロパティのシリアル化」を参照してください。

アプローチ2:JObjectsでJSONを操作する

を使用JsonConvert.SerializeObjectしてシリアル化を行う代わりに、configオブジェクトをにロードJObjectし、JSONから不要なプロパティを削除してから書き出します。ほんの数行の追加コードです。

JObject jo = JObject.FromObject(config);

// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();

json = jo.ToString();

アプローチ3:属性の賢い(ab)使用

  1. 適用する [JsonIgnore]シリアル化したくないプロパティに属性をます。
  2. 代替を追加し、 元のプロパティと同じタイプのプライベートプロパティセッターをクラスにます。そのプロパティの実装に元のプロパティを設定させます。
  3. 適用する [JsonProperty]属性を代替セッターにし、元のプロパティと同じJSON名を付けます。

ここに改訂されたConfigクラスがあります:

class Config
{
    [JsonIgnore]
    public Fizz ObsoleteSetting { get; set; }

    [JsonProperty("ObsoleteSetting")]
    private Fizz ObsoleteSettingAlternateSetter
    {
        // get is intentionally omitted here
        set { ObsoleteSetting = value; }
    }

    public Bang ReplacementSetting { get; set; }
}

7
私たちは、get-propertiesをinternalに設定することにより、プロジェクト(基本モデルの内部統合固有のスーパーセットを使用し、スーパークラスプロパティをシリアル化する必要がない)でそれを解決しました。パブリックセッターがあると、Web APIはプロパティを設定できましたが、プロパティのシリアル化を停止しました。
ダニエルサイディ2016年

7
使用に関連してJsonPropertyAttribute、C#6.0からは、使用することができますnameof代わりに「魔法の文字列」を使用してのキーワードを。これは、リファクタリングになり、多くが容易とばかプルーフを-あなたはすべての出現をリネームミスをすれば、プラス、コンパイラはとにかく、あなたに警告します。ブライアンの例@使用すると、使用法はこのようになります:[JsonProperty(nameof(ObsoleteSetting))]
ジェフ・ジェームズ

1
特にこのレガシーシナリオでは、JsonProperty宣言でnameof()を使用することはお勧めしません。JSONは、別のインターフェースとの外部の(そしてできれば永遠の)コントラクトを表します。リファクタリングする場合は、JSONプロパティの名前を変更したくはありません。この形式でJSONを生成する既存のすべてのJSONファイルとコンポーネントが互換性を失うことになります。実際、シリアル化されたすべてのプロパティにJsonProperty(…)を完全な名前で指定して、後でプロパティの名前を変更しても変更されないようにする方がよいでしょう。
弾薬Goettsch

36

逆シリアル化のみのプロパティを内部としてマークすることが許容される状況では、属性にまったく依存しない非常にシンプルなソリューションがあります。プロパティを内部取得としてマークするだけですが、パブリックセット:

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

これにより、デフォルトの設定/リゾルバーなどを使用して正しい逆シリアル化が行われますが、シリアル化された出力からプロパティが削除されます。


シンプルでありながら賢いソリューション。
hbulens

プロパティは検証モジュールによっても無視されることに注意してください。(したがって、これはパブリックgetメソッドに依存しているため、逆シリアル化のために[必須]としてマークすることはできません)。
Martin Hansen、

2
これはでもinternalでも機能しませんprivate。常にシリアル化されます。
ポール

これは私にはうまくいきませんでした。逆シリアル化中にプロパティが見つからないというエラーが発生しました。
ドリュー隅戸

31

私はこの属性にこだわるのが好きです。これは、プロパティを逆シリアル化する必要があるが、シリアル化しない場合、またはその逆の場合に使用する方法です。

ステップ1-カスタム属性を作成する

public class JsonIgnoreSerializationAttribute : Attribute { }

ステップ2-カスタムの契約Resloverを作成する

class JsonPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        //Return properties that do NOT have the JsonIgnoreSerializationAttribute
        return objectType.GetProperties()
                         .Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
                         .ToList<MemberInfo>();
    }
}

ステップ3-シリアライゼーションは必要ないがデシリアライゼーションは必要な場所に属性を追加する

    [JsonIgnoreSerialization]
    public string Prop1 { get; set; } //Will be skipped when serialized

    [JsonIgnoreSerialization]
    public string Prop2 { get; set; } //Also will be skipped when serialized

    public string Prop3 { get; set; } //Will not be skipped when serialized

ステップ4-使用する

var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });

お役に立てれば!また、逆シリアル化が行われるときにプロパティも無視されることにも注意してください。逆シリアル化を行っているときは、従来の方法でコンバーターを使用するだけです。

JsonConvert.DeserializeObject<MyType>(myString);

この有用な実装に感謝します。GetSerializableMembers完全にオーバーライドするのではなく、の基本実装を拡張する方法はありますか?
アラン、

4
return base.GetSerializableMembers(objectType).Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList();
アラン

なぜこれが一流の答えではないのかわかりません。クリーンで、newtonsoftのパターンに従い、簡単に実行できます。私が追加する唯一のものは、あなたがそれを使ってそれをグローバルに設定できるということですJsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() }
Matt M

1
気にしないでください、これは実際に質問者が求めていることを行いません。これは基本的にJsonIgnoreを再作成しています。GetSerializableMembersメソッドが読み取りまたは書き込みのどちらであるかを知る方法がないため、シリアル化のプロパティはスキップせず、シリアル化解除中にプロパティを設定して、両方のプロパティを除外します。このソリューションは機能しません。
Matt M

7

セッタープロパティを使用:

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }

[JsonIgnore]
private string _ignoreOnSerializing;

[JsonIgnore]
public string IgnoreOnSerializing
{
    get { return this._ignoreOnSerializing; }
    set { this._ignoreOnSerializing = value; }
}

この助けを願っています。


1
ありがとう。JsonPropertyはIgnoreOnSerializing、プロパティと同じ大文字にする必要があることに注意してください。nameof(IgnoreOnSerializing)名前を変更する場合は、マジックストリングを使用しないようにすることをお勧めします。
Bendik AugustNesbø17年

5

クラスプロパティにDe-Serializableであり、Serializableでないことを示すフラグを付ける方法を検索するのにかなり長い時間を費やした後、それを行うようなことはまったくないことがわかりました。したがって、2つの異なるライブラリまたはシリアル化手法(System.Runtime.Serialization.JsonとNewtonsoft.Json)を組み合わせたソリューションを思いついたので、次のように機能しました。

  • すべてのクラスとサブクラスに「DataContract」のフラグを付けます。
  • クラスとサブクラスのすべてのプロパティに「DataMember」のフラグを付けます。
  • クラスとサブクラスのすべてのプロパティに「JsonProperty」のフラグを付けます。ただし、シリアル化したくないプロパティは除きます。
  • シリアル化したくないプロパティに「JsonIgnore」のフラグを付けます。
  • 次に、「Newtonsoft.Json.JsonConvert.SerializeObject」を使用してシリアル化し、「System.Runtime.Serialization.Json.DataContractJsonSerializer」を使用して非シリアル化します。

    using System;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;
    
    namespace LUM_Win.model
    {
        [DataContract]
        public class User
        {
            public User() { }
            public User(String JSONObject)
            {
                MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
                DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
    
                User user = (User)dataContractJsonSerializer.ReadObject(stream);
                this.ID = user.ID;
                this.Country = user.Country;
                this.FirstName = user.FirstName;
                this.LastName = user.LastName;
                this.Nickname = user.Nickname;
                this.PhoneNumber = user.PhoneNumber;
                this.DisplayPicture = user.DisplayPicture;
                this.IsRegistred = user.IsRegistred;
                this.IsConfirmed = user.IsConfirmed;
                this.VerificationCode = user.VerificationCode;
                this.Meetings = user.Meetings;
            }
    
            [DataMember(Name = "_id")]
            [JsonProperty(PropertyName = "_id")]
            public String ID { get; set; }
    
            [DataMember(Name = "country")]
            [JsonProperty(PropertyName = "country")]
            public String Country { get; set; }
    
            [DataMember(Name = "firstname")]
            [JsonProperty(PropertyName = "firstname")]
            public String FirstName { get; set; }
    
            [DataMember(Name = "lastname")]
            [JsonProperty(PropertyName = "lastname")]
            public String LastName { get; set; }
    
            [DataMember(Name = "nickname")]
            [JsonProperty(PropertyName = "nickname")]
            public String Nickname { get; set; }
    
            [DataMember(Name = "number")]
            [JsonProperty(PropertyName = "number")]
            public String PhoneNumber { get; set; }
    
            [DataMember(Name = "thumbnail")]
            [JsonProperty(PropertyName = "thumbnail")]
            public String DisplayPicture { get; set; }
    
            [DataMember(Name = "registered")]
            [JsonProperty(PropertyName = "registered")]
            public bool IsRegistred { get; set; }
    
            [DataMember(Name = "confirmed")]
            [JsonProperty(PropertyName = "confirmed")]
            public bool IsConfirmed { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "verification_code")]
            public String VerificationCode { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "meeting_ids")]
            public List<Meeting> Meetings { get; set; }
    
            public String toJSONString()
            {
                return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            }
        }
    }

お役に立てば幸い...


1
Bravo Ahmed Abulazm。おかげで、多くの作業から私を救いました。:)
Sike12

2

@ThoHoのソリューションを参照すると、追加のタグなしで、実際に必要なのはセッターを使用することだけです。

私には以前、単一の参照IDがありました。それをロードして、参照IDの新しいコレクションに追加したかったのです。参照IDの定義を変更して、setterメソッドのみが含まれるようにし、新しいコレクションに値を追加しました。プロパティにgetがない場合、Jsonは値を書き戻すことができません方法。

// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }

// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }

このクラスは以前のバージョンと下位互換性があり、新しいバージョンのRefIdのみを保存します。


1

Tho Hoの答えを基にして、これはフィールドにも使用できます。

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }

[JsonIgnore]
public string IgnoreOnSerializing;

1

アプリケーションでこれが行われる場所に応じて、それが1つのプロパティだけの場合、手動でこれを行う1つの方法は、プロパティ値をnullに設定し、モデルで、値がnullの場合にプロパティを無視するように指定できます。

[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }

ASP.NET Core Webアプリで作業している場合、Startup.csファイルでこれを設定することにより、すべてのモデルのすべてのプロパティに対してグローバルに設定できます。

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

0

JsonConvertを使用している場合、IgnoreDataMemberAttributeは問題ありません。私の標準ライブラリはNewton.Jsonを参照していません。オブジェクトのシリアル化を制御するために[IgnoreDataMember]を使用しています。

Newton.netヘルプドキュメントから。


0

Json.netがシリアル化時にのみそれを無視し、逆シリアル化時にそれに注意を払うように、C#オブジェクトのプロパティを(たとえば属性で)マークする簡単な方法はありますか?

これを書いている時点で私が見つけた最も簡単な方法は、このロジックをIContractResolverに含めることです。

後世のために上記のリンクからのサンプルコードをここにコピーしました:

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e;
                };
        }

        return property;
    }
}

答えはすべて良いですが、このアプローチは最もクリーンな方法のように見えました。私が実際に実装したのは、SkipSerializeおよびSkipDeserializeのプロパティで属性を探すことです。これにより、制御するクラスをマークアップできます。すばらしい質問です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.