2012-05-04 18 views
11

私はオブジェクトの最初の深さレベルを望んでいます(私は子供が欲しくない)。私は利用可能な任意のライブラリを使用して喜んでです。ほとんどのライブラリは、単に無視するのではなく、再帰の深さに達したときに例外をスローします。これが不可能な場合は、特定のデータ型で特定のメンバーのシリアライゼーションを無視する方法がありますか?JSONオブジェクトをC#で特定の深度にシリアル化または逆シリアル化する方法は?

編集: のは、私はそうのような物を持っているとしましょう:

class MyObject 
{ 
    String name = "Dan"; 
    int age = 88; 
    List<Children> myChildren = ...(lots of children with lots of grandchildren); 
} 

私は(複雑なタイプの)子を削除したいこのようなオブジェクトを返すために:

class MyObject 
{ 
    String name = "Dan"; 
    int age = 88; 
    List<Children> myChildren = null; 
} 
+0

でこれを使用することができますか? – igofed

+1

子なしで新しいオブジェクトを作成してそれをシリアル化できますか? –

+0

論理的には意味がありますが、私はObject型のオブジェクトをその子要素から取り除きたいと思っています。私はjsonのシリアライゼーションがこれを行う最善の方法だと思っていましたが、私は間違いなく提案についています。 –

答えて

24

これは、可能な限りJson.NETで、JsonWriterとシリアライザのContractResolverとの間の調整を使用してください。

カスタムJsonWriterは、オブジェクトが開始されたときにカウンタをインクリメントし、終了すると再びデクリメントします。

public class CustomJsonTextWriter : JsonTextWriter 
{ 
    public CustomJsonTextWriter(TextWriter textWriter) : base(textWriter) {} 

    public int CurrentDepth { get; private set; } 

    public override void WriteStartObject() 
    { 
     CurrentDepth++; 
     base.WriteStartObject(); 
    } 

    public override void WriteEndObject() 
    { 
     CurrentDepth--; 
     base.WriteEndObject(); 
    } 
} 

カスタムContractResolverは、現在の深さを確認するために使用されるすべてのプロパティに特別なShouldSerialize述語を適用します。

次の方法は、これら2つのカスタムクラスがどのように連携して動作するかを示しています。

public static string SerializeObject(object obj, int maxDepth) 
{ 
    using (var strWriter = new StringWriter()) 
    { 
     using (var jsonWriter = new CustomJsonTextWriter(strWriter)) 
     { 
      Func<bool> include =() => jsonWriter.CurrentDepth <= maxDepth; 
      var resolver = new CustomContractResolver(include); 
      var serializer = new JsonSerializer {ContractResolver = resolver}; 
      serializer.Serialize(jsonWriter, obj); 
     } 
     return strWriter.ToString(); 
    } 
} 

次のテストコードでは、最大深度をそれぞれ1と2レベルに制限しています。

var obj = new Node { 
    Name = "one", 
    Child = new Node { 
     Name = "two", 
     Child = new Node { 
      Name = "three" 
     } 
    } 
}; 
var txt1 = SerializeObject(obj, 1); 
var txt2 = SerializeObject(obj, 2); 

public class Node 
{ 
    public string Name { get; set; } 
    public Node Child { get; set; } 
} 
+0

ライブラリJson.Netの実際のバージョンでこれを動作させることはできません。 CustomContractResolversメソッドが呼び出されることはないようです。 – Kjellski

+0

申し訳ありませんが、私は明示的に言う部分を逃しました:CreatePROPERTY ...悪いです。通常のメンバーにはもう少し詳しい情報が必要でしょうか?どんな解決策ですか? – Kjellski

+13

['JsonSerializerSettings.MaxDepth'プロパティ](http://james.newtonking.com/projects/json/help/html/P_Newtonsoft_Json_JsonSerializerSettings_MaxDepth.htm)はこれを気にしません – drzaus

1

あなたがオブジェクトを点検し、必要に応じて、各プロパティの値を変更コピーを作成するためにリフレクションを使用することができます。偶然、この種のことを本当に簡単にする新しい図書館を公開しました。あなたはここでそれを得ることができます:https://github.com/jamietre/IQObjectMapper

ここでは、オブジェクトの各プロパティを使用して

var newInstance = ObjectMapper.Map(obj,(value,del) => { 
    return value !=null && value.GetType().IsClass ? 
     null : 
     value; 
    }); 

「マップ」メソッドを反復を使用するコードの例だと、IDelegateInfoは反射を持つ(各Func<object,IDelegateInfo>を呼び出しますプロパティ名、タイプなどの情報)。この関数は、各プロパティの新しい値を返します。したがって、この例では、各プロパティの値をテストしてクラスであるかどうかを確認し、そうであればnullを返します。そうでない場合は、元の値を返します。それを行うため

別のより表現方法:いずれの場合も

var obj = new MyObject(); 

// map the object to a new dictionary   

var dict = ObjectMapper.ToDictionary(obj); 

// iterate through each item in the dictionary, a key/value pair 
// representing each property 

foreach (KeyValuePair<string,object> kvp in dict) { 
    if (kvp.Value!=null && kvp.Value.GetType().IsClass) { 
     dict[kvp.Key]=null; 
    } 
} 

// map back to an instance 

var newObject = ObjectMapper.ToNew<MyObject>(dict); 

newInstance.myChildrenの値(非値型付けされた他のプロパティ)はnullになります。このマッピングで何が起こるかのルールを簡単に変更することができます。

これが役に立ちます。 Btw - あなたのコメントから、JSONは本当にあなたの目標ではなく、あなたがそれを達成するのに役立つと思ったようなものだと思う。あなたがjsonになりたい場合は、これの出力をシリアル化してください。

string json = JavaScriptSerializer.Serialize(newObject); 

しかし、それは最後までただの手段だったされた場合、私はJSONを伴わないだろう。 CLRオブジェクトにとどまりたい場合は、JSONを仲介者として使用する必要はありません。

0

まず、すべてのクレジットがNathan Baulchに行くべきだと言いたいと思います。これはMaxDepthを設定に組み合わせた彼の答えの適応です。あなたの助けに感謝ネイサン!

using Newtonsoft.Json; 
using Newtonsoft.Json.Serialization; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Web; 
using System.Web.Mvc; 

namespace Helpers 
{ 
    public class JsonNetResult : JsonResult 
    { 
     public JsonNetResult() 
     { 
      Settings = new JsonSerializerSettings 
      { 
       ReferenceLoopHandling = ReferenceLoopHandling.Error 
      }; 
     } 

     public JsonSerializerSettings Settings { get; private set; } 

     public override void ExecuteResult(ControllerContext context) 
     { 
      if (context == null) 
       throw new ArgumentNullException("context"); 
      if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
       throw new InvalidOperationException("JSON GET is not allowed"); 

      HttpResponseBase response = context.HttpContext.Response; 
      response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType; 

      if (this.ContentEncoding != null) 
       response.ContentEncoding = this.ContentEncoding; 
      if (this.Data == null) 
       return; 

      var scriptSerializer = JsonSerializer.Create(this.Settings); 

      using (var sw = new StringWriter()) 
      { 
       if (Settings.MaxDepth != null) 
       { 
        using (var jsonWriter = new JsonNetTextWriter(sw)) 
        { 
         Func<bool> include =() => jsonWriter.CurrentDepth <= Settings.MaxDepth; 
         var resolver = new JsonNetContractResolver(include); 
         this.Settings.ContractResolver = resolver; 
         var serializer = JsonSerializer.Create(this.Settings); 
         serializer.Serialize(jsonWriter, Data); 
        } 
        response.Write(sw.ToString()); 
       } 
       else 
       { 
        scriptSerializer.Serialize(sw, this.Data); 
        response.Write(sw.ToString()); 
       } 
      } 
     } 
    } 

    public class JsonNetTextWriter : JsonTextWriter 
    { 
     public JsonNetTextWriter(TextWriter textWriter) : base(textWriter) { } 

     public int CurrentDepth { get; private set; } 

     public override void WriteStartObject() 
     { 
      CurrentDepth++; 
      base.WriteStartObject(); 
     } 

     public override void WriteEndObject() 
     { 
      CurrentDepth--; 
      base.WriteEndObject(); 
     } 
    } 

    public class JsonNetContractResolver : DefaultContractResolver 
    { 
     private readonly Func<bool> _includeProperty; 

     public JsonNetContractResolver(Func<bool> includeProperty) 
     { 
      _includeProperty = includeProperty; 
     } 

     protected override JsonProperty CreateProperty(
      MemberInfo member, MemberSerialization memberSerialization) 
     { 
      var property = base.CreateProperty(member, memberSerialization); 
      var shouldSerialize = property.ShouldSerialize; 
      property.ShouldSerialize = obj => _includeProperty() && 
               (shouldSerialize == null || 
               shouldSerialize(obj)); 
      return property; 
     } 
    } 
} 

用途:

// instantiating JsonNetResult to handle circular reference issue. 
var result = new JsonNetResult 
{ 
    Data = <<The results to be returned>>, 
    JsonRequestBehavior = JsonRequestBehavior.AllowGet, 
    Settings = 
     { 
      ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 
      MaxDepth = 1 
     } 
}; 

return result; 
0

あなたはASP.NETコアプロジェクトでこれを使用したい場合は、多分あなたはあなた自身のJsonTextWriterを実装傾けます。しかし、あなたはカスタムDefaultContractResolverと

using Newtonsoft.Json; 
using Newtonsoft.Json.Serialization; 
using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Linq; 

namespace customserialization 
{ 
    /// <summary> 
    /// IValueProvider personalizado para manejar max depth level 
    /// </summary> 
    public class CustomDynamicValueProvider : DynamicValueProvider, IValueProvider 
    { 
     MemberInfo _memberInfo; 
     MaxDepthHandler _levelHandler; 

     public CustomDynamicValueProvider(MemberInfo memberInfo, MaxDepthHandler levelHandler) : base(memberInfo) 
     { 
      _memberInfo = memberInfo; 
      _levelHandler = levelHandler; 
     } 

     public new object GetValue(object target) 
     { 
      //Si el valor a serializar es un objeto se incrementa el nivel de profundidad. En el caso de las listas el nivel se incrementa en el evento OnSerializing 
      if (((PropertyInfo)_memberInfo).PropertyType.IsClass) this._levelHandler.IncrementLevel(); 

      var rv = base.GetValue(target); 

      //Al finalizar la obtención del valor se decrementa. En el caso de las listas el nivel se decrementa en el evento OnSerialized 
      if (((PropertyInfo)_memberInfo).PropertyType.IsClass) this._levelHandler.DecrementLevel(); 

      return rv; 
     } 
    } 

    /// <summary> 
    /// Maneja los niveles de serialización 
    /// </summary> 
    public class MaxDepthHandler 
    { 
     int _maxDepth; 
     int _currentDepthLevel; 

     /// <summary> 
     /// Nivel actual 
     /// </summary> 
     public int CurrentDepthLevel { get { return _currentDepthLevel; } } 

     public MaxDepthHandler(int maxDepth) 
     { 
      this._currentDepthLevel = 1; 
      this._maxDepth = maxDepth; 
     } 

     /// <summary> 
     /// Incrementa el nivel actual 
     /// </summary> 
     public void IncrementLevel() 
     { 
      this._currentDepthLevel++; 
     } 

     /// <summary> 
     /// Decrementa el nivel actual 
     /// </summary> 
     public void DecrementLevel() 
     { 
      this._currentDepthLevel--; 
     } 

     /// <summary> 
     /// Determina si se alcanzó el nivel actual 
     /// </summary> 
     /// <returns></returns> 
     public bool IsMaxDepthLevel() 
     { 
      return !(this._currentDepthLevel < this._maxDepth); 
     } 
    } 

    public class ShouldSerializeContractResolver : DefaultContractResolver 
    { 

     MaxDepthHandler _levelHandler; 

     public ShouldSerializeContractResolver(int maxDepth) 
     { 
      this._levelHandler = new MaxDepthHandler(maxDepth); 
     } 


     void OnSerializing(object o, System.Runtime.Serialization.StreamingContext context) 
     { 
      //Antes de serializar una lista se incrementa el nivel. En el caso de los objetos el nivel se incrementa en el método GetValue del IValueProvider 
      if (o.GetType().IsGenericList()) 
       _levelHandler.IncrementLevel(); 
     } 

     void OnSerialized(object o, System.Runtime.Serialization.StreamingContext context) 
     { 
      //Despues de serializar una lista se decrementa el nivel. En el caso de los objetos el nivel se decrementa en el método GetValue del IValueProvider 
      if (o.GetType().IsGenericList()) 
       _levelHandler.DecrementLevel(); 
     } 

     protected override JsonContract CreateContract(Type objectType) 
     { 
      var contract = base.CreateContract(objectType); 
      contract.OnSerializingCallbacks.Add(new SerializationCallback(OnSerializing)); 
      contract.OnSerializedCallbacks.Add(new SerializationCallback(OnSerialized)); 

      return contract; 
     } 


     protected override IValueProvider CreateMemberValueProvider(MemberInfo member) 
     { 
      var rv = base.CreateMemberValueProvider(member); 

      if (rv is DynamicValueProvider) //DynamicValueProvider es el valueProvider usado en general 
      { 
       //Utilizo mi propio ValueProvider, que utilizar el levelHandler 
       rv = new CustomDynamicValueProvider(member, this._levelHandler); 
      } 

      return rv; 
     } 

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

      var isObjectOrList = ((PropertyInfo)member).PropertyType.IsGenericList() || ((PropertyInfo)member).PropertyType.IsClass; 



      property.ShouldSerialize = 
        instance => 
        { 
         var shouldSerialize = true; 
         //Si se alcanzo el nivel maximo y la propiedad (member) actual a serializar es un objeto o lista no se serializa (shouldSerialize = false) 
         if (_levelHandler.IsMaxDepthLevel() && isObjectOrList) 
          shouldSerialize = false;       

         return shouldSerialize; 
        }; 

      return property; 
     } 



    } 

    public static class Util 
    { 
     public static bool IsGenericList(this Type type) 
     { 
      foreach (Type @interface in type.GetInterfaces()) 
      { 
       if (@interface.IsGenericType) 
       { 
        if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>)) 
        { 
         // if needed, you can also return the type used as generic argument 
         return true; 
        } 
       } 
      } 
      return false; 
     } 
    } 
} 

IValueProvider

とは、あなたが問題JSONのいくつかの例を示すことができ、あなたのコントローラ

 [HttpGet] 
     public IActionResult TestJSON() 
     { 
      var obj = new Thing 
      { 
       id = 1, 
       reference = new Thing 
       { 
        id = 2, 
        reference = new Thing 
        { 
         id = 3, 
         reference = new Thing 
         { 
          id = 4 
         } 
        } 
       } 
      }; 
      var settings = new JsonSerializerSettings() 
      { 
       ContractResolver = new ShouldSerializeContractResolver(2), 
      }; 

      return new JsonResult(obj, settings); 

     }