2012-05-04 17 views
11

मुझे केवल एक वस्तु का पहला गहराई स्तर चाहिए (मुझे कोई बच्चा नहीं चाहिए)। मैं उपलब्ध किसी पुस्तकालय का उपयोग करने के लिए तैयार हूँ। अधिकांश पुस्तकालय केवल अनदेखा करने के बजाए रिकर्सन गहराई तक पहुंचने पर अपवाद फेंक देंगे। यदि यह संभव नहीं है, तो क्या कुछ निश्चित सदस्यों को दिए गए कुछ सदस्यों के क्रमबद्धरण को अनदेखा करने का कोई तरीका है?सी # में एक निश्चित गहराई के लिए JSON ऑब्जेक्ट को क्रमबद्ध या deserialize कैसे करें?

संपादित करें:

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

में इस का उपयोग कर सकते हैं समस्या JSON के कुछ उदाहरण दिखा सकते हैं? – igofed

+1

क्या आप किसी भी बच्चे के बिना बस एक नई वस्तु बना सकते हैं और उसे क्रमबद्ध कर सकते हैं? –

+0

तार्किक रूप से यह समझ में आता है, लेकिन मैं अपने बच्चों के ऑब्जेक्ट के प्रकार की किसी वस्तु को पट्टी करना चाहता हूं। मुझे लगा कि जेसन सीरियलाइजेशन ऐसा करने का सबसे अच्छा तरीका होगा, लेकिन मैं सुझावों के लिए निश्चित रूप से खुला हूं। –

उत्तर

24

यह वह जगह है:

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

मैं (यहां तक ​​कि जटिल प्रकार) इस तरह एक वस्तु लौटने के लिए किसी भी बच्चों को निकालना चाहते हैं: चलो कहते हैं कि मैं बहुत की तरह एक वस्तु करते हैं 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 विधेय लागू होता है।

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

    public CustomContractResolver(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; 
    } 
} 

निम्न विधि दिखाती है कि ये दो कस्टम वर्ग एक साथ कैसे काम करते हैं।

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 के वास्तविक संस्करण में काम करने के लिए नहीं मिल सकता। ऐसा लगता है कि कस्टमकंट्रैक्ट रिसेल्वर विधियों को कभी नहीं बुलाया जाता है। – 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> कॉल (संपत्ति का नाम, प्रकार, आदि जैसी जानकारी)। फ़ंक्शन प्रत्येक प्रॉपर्टी के लिए नया मान देता है। तो इस उदाहरण में, मैं सिर्फ यह देखने के लिए प्रत्येक संपत्ति के मूल्य का परीक्षण करता हूं कि यह कक्षा है या नहीं, और यदि ऐसा है, तो शून्य वापस करें; यदि नहीं, तो मूल मान वापस करें।

एक और अधिक अर्थपूर्ण तरीका यह करने के लिए:

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 का मूल्य (और किसी भी अन्य गुण है कि गैर मूल्य टाइप कर रहे हैं) अशक्त हो जाएगा। आप इस मैपिंग में क्या होता है इसके लिए नियम आसानी से बदल सकते हैं।

उम्मीद है कि इससे मदद मिलती है। बीटीडब्ल्यू - आपकी टिप्पणी से ऐसा लगता है कि जेएसओएन वास्तव में आपका लक्ष्य नहीं है, लेकिन केवल कुछ ऐसा जो आपको लगता है वह आपको प्राप्त करने में मदद करेगा। यदि आप जेसन के साथ समाप्त करना चाहते हैं, तो बस इसके आउटपुट को क्रमबद्ध करें, उदा।

string json = JavaScriptSerializer.Serialize(newObject); 

लेकिन मैं json शामिल नहीं होगा अगर वह यह है कि एक को समाप्त करने के लिए सिर्फ एक साधन था, यदि आप सीएलआर वस्तुओं में रहना चाहते हैं तो जेएसओएन को मध्यस्थ के रूप में उपयोग करने की कोई वास्तविक आवश्यकता नहीं है।

0

सबसे पहले, मैं कहना चाहता था कि सभी क्रेडिट नाथन बाउच जाना चाहिए। यह सेटिंग्स में मैक्सडेप का उपयोग करने के साथ संयुक्त उनके उत्तर का एक अनुकूलन है। आपकी मदद के लिए धन्यवाद नाथन!

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 और IValueProvider

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; 
     } 
    } 
} 

और अपने नियंत्रक

 [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); 

     } 
संबंधित मुद्दे