63

クラスの特定のプロパティをシリアル化する方法/かどうかを制御できる必要があります。最も単純なケースは[ScriptIgnore]. ただし、これらの属性は、私が取り組んでいるこの 1 つの特定のシリアライゼーションの状況でのみ尊重されるようにしたいと考えています。アプリケーションの下流にある他のモジュールもこれらのオブジェクトをシリアライズしたい場合、これらの属性のいずれも邪魔になるべきではありません。

したがって、私の考えはMyAttribute、プロパティでカスタム属性を使用し、その属性を探すことを知っているフックで JsonSerializer の特定のインスタンスを初期化することです。

一見したところ、JSON.NET で利用可能なフック ポイントのいずれもPropertyInfo、現在のプロパティがそのような検査を行うための を提供しているとは思えません。プロパティの値のみです。何か不足していますか?または、これにアプローチするより良い方法はありますか?

4

7 に答える 7

74

受け入れられた回答に基づいた、一般的な再利用可能な「プロパティを無視する」リゾルバーを次に示します。

/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties.  See https://stackoverflow.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
    protected readonly Dictionary<Type, HashSet<string>> Ignores;

    public IgnorableSerializerContractResolver() {
        this.Ignores = new Dictionary<Type, HashSet<string>>();
    }

    /// <summary>
    /// Explicitly ignore the given property(s) for the given type
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName">one or more properties to ignore.  Leave empty to ignore the type entirely.</param>
    public void Ignore(Type type, params string[] propertyName) {
        // start bucket if DNE
        if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();

        foreach (var prop in propertyName) {
            this.Ignores[type].Add(prop);
        }
    }

    /// <summary>
    /// Is the given property for the given type ignored?
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool IsIgnored(Type type, string propertyName) {
        if (!this.Ignores.ContainsKey(type)) return false;

        // if no properties provided, ignore the type entirely
        if (this.Ignores[type].Count == 0) return true;

        return this.Ignores[type].Contains(propertyName);
    }

    /// <summary>
    /// The decision logic goes here
    /// </summary>
    /// <param name="member"></param>
    /// <param name="memberSerialization"></param>
    /// <returns></returns>
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (this.IsIgnored(property.DeclaringType, property.PropertyName)
        // need to check basetype as well for EF -- @per comment by user576838
        || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)) {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}

そして使用法:

var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
于 2013-01-24T20:28:17.197 に答える
71

属性を使用しJsonIgnoreます。

たとえば、除外するにはId:

public class Person {
    [JsonIgnore]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
于 2014-10-10T22:46:07.373 に答える
50

いくつかのオプションがあります。以下を読む前に、この件に関するJson.Net のドキュメント記事を読むことをお勧めします。

この記事では、次の 2 つの方法を紹介します。

  1. boolプロパティをシリアル化するかどうかを決定するために Json.Net が従う命名規則に基づいて値を返すメソッドを作成します。
  2. プロパティを無視するカスタム コントラクト リゾルバーを作成します。

2つのうち、私は後者を支持します。属性を完全にスキップします。すべての形式のシリアライゼーションでプロパティを無視するためにのみ使用します。代わりに、問題のプロパティを無視するカスタム コントラクト リゾルバーを作成し、プロパティを無視する場合にのみコントラクト リゾルバーを使用して、クラスの他のユーザーが自由にプロパティをシリアル化できるようにするか、自分の気まぐれでなくしてください。

編集リンクの腐敗を避けるために、記事から問題のコードを投稿しています

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 =>
         {
            // replace this logic with your own, probably just  
            // return false;
            Employee e = (Employee)instance;
            return e.Manager != e;
         };
      }

      return property;
   }
}
于 2012-11-27T16:04:26.710 に答える
3

プロパティ名を文字列として設定することは気にしません。変更すると他のコードが壊れてしまうからです。

シリアル化する必要のあるオブジェクトにはいくつかの「表示モード」があったため、最終的にコントラクト リゾルバーで次のようなことを行いました (コンストラクター引数によって提供される表示モード)。

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
    JsonProperty property = base.CreateProperty(member, memberSerialization);
    if (viewMode == ViewModeEnum.UnregisteredCustomer && member.GetCustomAttributes(typeof(UnregisteredCustomerAttribute), true).Length == 0)
    {
        property.ShouldSerialize = instance => { return false; };
    }

    return property;
}

私のオブジェクトは次のようになります。

public interface IStatement
{
    [UnregisteredCustomer]
    string PolicyNumber { get; set; }

    string PlanCode { get; set; }

    PlanStatus PlanStatus { get; set; }

    [UnregisteredCustomer]
    decimal TotalAmount { get; }

    [UnregisteredCustomer]
    ICollection<IBalance> Balances { get; }

    void SetBalances(IBalance[] balances);
}

これの欠点は、リゾルバーでのリフレクションのビットですが、より保守しやすいコードを使用する価値があると思います。

于 2014-03-07T14:27:56.507 に答える
1

drzaus と Steve Rukuts の両方の回答を組み合わせて、良い結果が得られました。ただし、JsonPropertyAttribute を別の名前またはプロパティのキャップで設定すると、問題に直面します。例えば:

[JsonProperty("username")]
public string Username { get; set; }

UnderlyingName を考慮に入れると、問題が解決します。

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

    if (this.IsIgnored(property.DeclaringType, property.PropertyName)
        || this.IsIgnored(property.DeclaringType, property.UnderlyingName)
        || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)
        || this.IsIgnored(property.DeclaringType.BaseType, property.UnderlyingName))
    {
        property.ShouldSerialize = instance => { return false; };
    }

    return property;
}
于 2017-11-29T18:01:08.670 に答える
0

F# を使用する場合 (または単に C# 用に最適化されていない API を使用する場合)、FSharp.JsonSkippableライブラリを使用すると、シリアル化時に特定のプロパティを含めるかどうかを単純かつ厳密に型指定された方法で制御できます (また、プロパティが使用されたかどうかを判断します)。さらに、null 可能性の除外を個別に制御/決定します。(完全な開示: 私はライブラリの作成者です。)

于 2018-11-04T17:25:22.653 に答える