2016-06-26 6 views
0

WebApiプロジェクトでのセキュリティ処理を簡略化するためにIContractResolverを作成しようとしています。JSON.Netカスタム契約のシリアル化とコレクション

私は何をしようとしています:

は、私は(例については、エンドポイントと呼ばれるユーザーの役割)を動的条件のセットに基づいて特定のオブジェクト/プロパティをシリアル化します。

私はインターフェイスのCreatePropertyオーバーライドでチェックされ、独自のロジックにShouldSerialize関数を設定するカスタム属性を実装しました。

私の質問は、条件付きで特定のリストにあるオブジェクトを完全にシリアル化することは可能ですか?前処理ステップ(エラーが発生しやすく、オブジェクトを変更した場合)でリストをフィルタリングする代わりに、現在のContractResolverによって再帰的に処理されることを希望します。私はこのような何かを取得しようとしていたように

override void CreateObject(JSONObject ob){ 
if (ob.DeclaringType == MyType) 
{ 
    ob.ShouldSerialize = instance => {[...] }; //Custom Logic 
} 
} 

私は上書きをしないのですが、これは全く可能ではないでしょうか?実際にこれを行うより良い方法はありますか?すべての値を「あらかじめ解析する」必要はありませんか?

+0

これはそのままでは実装されていません。 ['JsonSerializerInternalWriter.SerializeList()'](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs#L663)のソースを参照してください。しかし、何かが可能かもしれません。あなたはあなたのセットアップの[mcve]を与えることができますか?シリアライズ対デシリアライズ時に異なる契約リゾルバを使用できますか?あなたのルートオブジェクトはフィルタリングを必要とするコレクションですか、それともプロパティコレクションをフィルタリングする必要がありますか? – dbc

答えて

0

これはそのままでは実装されていません。 JsonSerializerInternalWriter.SerializeList()のソースを調べると、いくつかのフィルターに基づいてコレクション項目をスキップするロジックがないことがわかります。

ただし、Json.NETにはrobust exception handlingがあります。例外がスローされた場合は、[OnError]コールバックでキャッチされ、飲み込まオブジェクトシリアル化するために始めたときに:配列のエントリを書いている場合

  • を、配列のエントリがは(あなたの希望の動作)をスキップされます。
  • ルートオブジェクトを書き込んだ場合、例外がキャッチされません(おそらくバグですか?)
  • それ以外の場合はnullが書き込まれます。

このように、あなたの所望の機能を達成するための一つの可能​​性は、人工的なコールバックからの例外をスローすることですが、その後、ハンドラで例外をキャッチして飲み込むがJsonContract.OnErrorCallbacksに追加し、カスタム契約リゾルバによってJsonContract.OnSerializingCallbacksに加えます。すでに行っているようにプロパティ値のフィルタリングと組み合わせると、この方法は、ルートオブジェクトである場合でも、辞書、動的オブジェクト、または多次元配列に含まれている場合でも、シークレットオブジェクトを直列化できないことを保証するという利点があります。このアプローチはPreserveReferencesHandling.Arraysを妨げません。次のようにこれを行い

一つの契約リゾルバは次のとおりです。

sealed class JsonSkipObjectException : JsonException 
{ 
} 

public class ShouldSerializeContractResolver : DefaultContractResolver 
{ 
    readonly Predicate<object> shouldSerialize; 
    readonly SerializationCallback serializationCallback; 
    readonly SerializationErrorCallback onErrorCallback; 

    public ShouldSerializeContractResolver(Predicate<object> shouldSerialize) 
     : base() 
    { 
     this.shouldSerialize = shouldSerialize; 
     this.serializationCallback = (o, context) => 
      { 
       if (shouldSerialize != null && !this.shouldSerialize(o)) 
        throw new JsonSkipObjectException(); 
      }; 
     this.onErrorCallback = (o, context, errorContext) => 
      { 
       if (errorContext.Error is JsonSkipObjectException) 
       { 
        errorContext.Handled = true; 
       } 
      }; 
    } 

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

     if (shouldSerialize != null) 
     { 
      if (property.Readable) 
      { 
       var oldShouldSerialize = property.ShouldSerialize; 
       property.ShouldSerialize = (o) => 
        { 
         if (oldShouldSerialize != null && !oldShouldSerialize(o)) 
          return false; 
         var value = property.ValueProvider.GetValue(o); 
         if (!this.shouldSerialize(value)) 
          return false; 
         return true; 
        }; 
      } 
     } 
     return property; 
    } 

    protected override JsonContract CreateContract(Type objectType) 
    { 
     var contract = base.CreateContract(objectType); 
     contract.OnSerializingCallbacks.Add(serializationCallback); 
     contract.OnErrorCallbacks.Add(onErrorCallback); 
     return contract; 
    } 
} 

そして、一つの可能​​な用途は次のようになります。

public interface IConditionalSerialization 
{ 
    bool ShouldSerialize(); 
} 

public class ConditionalSerializationObject : IConditionalSerialization 
{ 
    public bool IsSecret { get; set; } 

    public string SecretProperty { get { return "should not see me"; } } 

    // Ensure "normal" conditional property serialization is not broken 
    public bool ShouldSerializeSecretProperty() 
    { 
     return false; 
    } 

    #region IConditionalSerialization Members 

    bool IConditionalSerialization.ShouldSerialize() 
    { 
     return !IsSecret; 
    } 

    #endregion 
} 

public class TestClass 
{ 
    public static void Test() 
    { 
     Predicate<object> filter = (o) => 
      { 
       var conditional = o as IConditionalSerialization; 
       return conditional == null || conditional.ShouldSerialize(); 
      }; 
     var settings = new JsonSerializerSettings 
     { 
      ContractResolver = new ShouldSerializeContractResolver(filter), 
     }; 

     var ok = new ConditionalSerializationObject { IsSecret = false }; 
     var notOk = new ConditionalSerializationObject { IsSecret = true }; 

     Test(ok, settings); 
     Test(new { Public = ok, Private = notOk }, settings); 
     Test(new [] { ok, notOk, ok, notOk }, settings); 
     Test(new[,] {{ ok, notOk, ok, notOk }}, settings); 
     Test(new { Array = new[,] { { ok, notOk, ok, notOk } } }, settings); 
     try 
     { 
      Test(notOk, settings); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Exception thrown and not caught serializing root object " + notOk.GetType()); 
      Console.WriteLine(ex); 
     } 
    } 

    static void Test<T>(T value, JsonSerializerSettings settings) 
    { 
     Console.WriteLine("Unfiltered object: "); 
     Console.WriteLine(JToken.FromObject(value)); 

     var serializer = JsonSerializer.CreateDefault(settings); 
     var token = JToken.FromObject(value, serializer); 
     Console.WriteLine("Filtered object: "); 
     Console.WriteLine(token); 
     if (!token.SelectTokens("..IsSecret").All(t => JToken.DeepEquals(t, (JValue)false))) 
     { 
      throw new InvalidOperationException("token.SelectTokens(\"..IsSecret\").All(t => JToken.DeepEquals(t, (JValue)true))"); 
     } 
     if (token.SelectTokens("..SecretProperty").Any()) 
     { 
      throw new InvalidOperationException("token.SelectTokens(\"..SecretProperty\").Any()"); 
     } 
     Console.WriteLine("Secret objects and properties were successfully filtered."); 
     Console.WriteLine(""); 
    } 
} 

プロトタイプfiddle

多数の例外をスローしてキャッチすることは、パフォーマンスに影響を与える可能性があることに注意してください。 How expensive are exceptions in C#?を参照してください。これが問題かどうかを判断するには、Webアプリケーションのプロファイルを作成する必要があります。また、「秘密」のルートオブジェクトを直列化しようとする際に、Webサービスが例外を返すかどうかを決定する必要があります。

+0

これは完璧な答えです.Json.Netはコレクションオブジェクトをスキップできません。しかしあなたの道は完全に働くかもしれません。 これは私のユースケースでうまくいくかどうかを見るためにパフォーマンスをプロファイルする必要がありますが、私が求めていたものはかなりです。 – Morphex

関連する問題