2012-09-20 8 views
13

के आधार पर एक संपत्ति को क्रमबद्ध रूप से क्रमबद्ध करें, मैं जेनरेटेड जेसन से सीरियलाइजेशन के समय अपने मूल्य के आधार पर एक संपत्ति को शामिल या छोड़ना चाहता हूं।वैकल्पिक रूप से अपने रनटाइम मान

अधिक-विशेष रूप से, मैं एक प्रकार अगर एक मूल्य इसे करने के लिए सौंपा गया है और मैं केवल उस प्रकार के गुण को क्रमानुसार करने अगर वहाँ है कुछ किया गया यह करने के लिए आवंटित (चाहते हैं जानता है कि तो मैं मूल्य का निरीक्षण करने की जरूरत है चलने के समय पर)। मैं अपने एपीआई के लिए "डिफ़ॉल्ट मान है" और "बिल्कुल निर्दिष्ट नहीं था" के बीच अंतर का पता लगाने के लिए इसे आसान बनाने की कोशिश कर रहा हूं।

एक कस्टम जेसन कनवर्टर पर्याप्त प्रतीत नहीं होता है; मैंने कोशिश की और मुझे विश्वास है कि कनवर्टर कहने से पहले संपत्ति का नाम पहले से ही क्रमबद्ध है। मेरे मामले में मैं संपत्ति का नाम भी छोड़ना चाहता हूं।

मैंने डिफॉल्ट कंट्रैक्ट रेसोलवर को विस्तारित करने के लिए देखा है लेकिन CreateProperty और CreateProperties (जो जेसनप्रोपर्टी सीरियलाइजेशन मेटाडाटा लौटाता है) केवल टाइप टाइपिंग क्रमबद्ध होता है, इसलिए मैं इंस्टेंस का निरीक्षण नहीं कर सकता। आम तौर पर, मुझे डिफॉल्ट कंट्रैक्ट रिसेल्वर पर कुछ भी दिखाई नहीं देता है, जिससे को नियंत्रित करने की इजाजत मिलती है यदि एक उदाहरण धारावाहिक है; शायद मैं इसे याद किया।

मैं भी हो सकता है सोचा था कि मैं एक ContractResolver कि मेरी प्रकार के लिए एक कस्टम JsonObjectContract लौटे बनाने के लिए आवश्यक। लेकिन, फिर से, मुझे JsonObjectContract पर कुछ भी दिखाई नहीं देता है जो किसी उदाहरण के आधार पर निर्णय लेता है।

क्या मेरा लक्ष्य पूरा करने का कोई अच्छा तरीका है? क्या मैं बस कुछ आसान याद कर रहा हूँ? कोई भी सहायता जो आप प्रदान कर सकते हैं उसकी बहुत सराहना की जाती है। चूंकि जेसन.NET इतना एक्स्टेंसिबल है, मैंने सोचा कि यह बहुत कठिन नहीं होगा। लेकिन मुझे लगता है कि मैं खरबूजे में रास्ता बंद कर रहा हूँ। :)

+0

यह सुनिश्चित करना बेहतर नहीं होगा कि आप जो डेटा खिला रहे हैं वह धारावाहिकरण के लिए उचित प्रारूप में है? डेटा को वैसे ही प्राप्त करने के लिए विशेष रूप से एक LINQ क्वेरी में एक अनौपचारिक प्रकार या प्रक्षेपण का उपयोग करें। –

+0

@ रिकस्ट्राहल - मैं एम्बर के साथ काम करता हूं। कारण यह काम नहीं करेगा कि क्षेत्र शामिल या बहिष्कृत करने के लिए गतिशील हैं, और प्रोटोकॉल का हिस्सा हम उपयोग कर रहे हैं। एक और तरीका रखो, एक क्षेत्र जो धारावाहिकरण में शामिल नहीं है, कुछ मूल्य के साथ दिखाई देने वाले क्षेत्र से कुछ अलग है। –

उत्तर

7

ठीक है, थोड़ी देर के लिए Json.NET स्रोत में चारों ओर खुदाई के बाद, मैं अंत में यह काम कर गया और यह और भी ShouldSerialize निर्दिष्ट सदस्यों को बताया कि Json.NET का समर्थन करता है का सम्मान करेगा * और *। चेतावनी दी: यह निश्चित रूप से खरपतवार में जा रहा है।

तो मुझे एहसास हुआ कि JsonProperty वर्ग DefaultContractResolver.CreateProperty द्वारा दिया ShouldSerialize और कनवर्टर गुण है, जो मुझे अगर संपत्ति उदाहरण वास्तव में श्रृंखलाबद्ध किया जाना चाहिए निर्दिष्ट करने देते है और, यदि हां, कैसे यह करने के लिए।

Deserialization कुछ अलग करने की आवश्यकता है, हालांकि। DefaultContractResolver.ResolveContract डिफ़ॉल्ट रूप से कस्टम प्रकार के लिए, एक null कनवर्टर संपत्ति के साथ एक JsonObjectContract वापस करेगा। मेरे प्रकार को सही तरीके से deserialize करने के लिए, जब मेरे अनुबंध के लिए अनुबंध है तो मुझे कनवर्टर संपत्ति सेट करने की आवश्यकता है।

यहां कोड है (त्रुटि को संभालने/आदि को चीजों को जितना संभव हो सके रखने के लिए हटा दिया गया है)।

पहले, प्रकार है कि विशेष हैंडलिंग की जरूरत है:

public struct Optional<T> 
{ 
    public readonly bool ValueProvided; 
    public readonly T Value; 

    private Optional(T value) 
    { 
     this.ValueProvided = true; 
     this.Value = value; 
    } 

    public static implicit operator Optional<T>(T value) 
    { 
     return new Optional<T>(value); 
    } 
} 

और वहाँ कनवर्टर कि यह ठीक से क्रमानुसार जाएगा के बाद हम जानते हैं कि यह श्रृंखलाबद्ध किया जाना चाहिए है:

public class OptionalJsonConverter<T> : JsonConverter 
{ 
    public static OptionalJsonConverter<T> Instance = new OptionalJsonConverter<T>(); 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var optional = (Optional<T>)value; // Cast so we can access the Optional<T> members 
     serializer.Serialize(writer, optional.Value); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     var valueType = objectType.GetGenericArguments()[ 0 ]; 
     var innerValue = (T)serializer.Deserialize(reader, valueType); 
     return (Optional<T>)innerValue; // Explicitly invoke the conversion from T to Optional<T> 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(Optional<T>); 
    } 
} 

अंत में, और सबसे -सबसेली, यहां अनुबंध रेसोलवर है जो हुक को सम्मिलित करता है:

public class CustomContractResolver : DefaultContractResolver 
{ 
    // For deserialization. Detect when the type is being deserialized and set the converter for it. 
    public override JsonContract ResolveContract(Type type) 
    { 
     var contract = base.ResolveContract(type); 
     if(contract.Converter == null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>)) 
     { 
      // This may look fancy but it's just calling GetOptionalJsonConverter<T> with the correct T 
      var optionalValueType = type.GetGenericArguments()[ 0 ]; 
      var genericMethod = this.GetAndMakeGenericMethod("GetOptionalJsonConverter", optionalValueType); 
      var converter = (JsonConverter)genericMethod.Invoke(null, null); 
      // Set the converter for the type 
      contract.Converter = converter; 
     } 
     return contract; 
    } 

    public static OptionalJsonConverter<T> GetOptionalJsonConverter<T>() 
    { 
     return OptionalJsonConverter<T>.Instance; 
    } 

    // For serialization. Detect when we're creating a JsonProperty for an Optional<T> member and modify it accordingly. 
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     var jsonProperty = base.CreateProperty(member, memberSerialization); 
     var type = jsonProperty.PropertyType; 
     if(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>)) 
     { 
      // This may look fancy but it's just calling SetJsonPropertyValuesForOptionalMember<T> with the correct T 
      var optionalValueType = type.GetGenericArguments()[ 0 ]; 
      var genericMethod = this.GetAndMakeGenericMethod("SetJsonPropertyValuesForOptionalMember", optionalValueType); 
      genericMethod.Invoke(null, new object[]{ member.Name, jsonProperty }); 
     } 
     return jsonProperty; 
    } 

    public static void SetJsonPropertyValuesForOptionalMember<T>(string memberName, JsonProperty jsonProperty) 
    { 
     if(jsonProperty.ShouldSerialize == null) // Honor ShouldSerialize* 
     { 
      jsonProperty.ShouldSerialize = 
       (declaringObject) => 
       { 
        if(jsonProperty.GetIsSpecified != null && jsonProperty.GetIsSpecified(declaringObject)) // Honor *Specified 
        { 
         return true; 
        }      
        object optionalValue; 
        if(!TryGetPropertyValue(declaringObject, memberName, out optionalValue) && 
         !TryGetFieldValue(declaringObject, memberName, out optionalValue)) 
        { 
         throw new InvalidOperationException("Better error message here"); 
        } 
        return ((Optional<T>)optionalValue).ValueProvided; 
       }; 
     } 
     if(jsonProperty.Converter == null) 
     { 
      jsonProperty.Converter = CustomContractResolver.GetOptionalJsonConverter<T>(); 
     } 
    } 

    // Utility methods used in this class 
    private MethodInfo GetAndMakeGenericMethod(string methodName, params Type[] typeArguments) 
    { 
     var method = this.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); 
     return method.MakeGenericMethod(typeArguments); 
    } 

    private static bool TryGetPropertyValue(object declaringObject, string propertyName, out object value) 
    { 
     var propertyInfo = declaringObject.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 
     if(propertyInfo == null) 
     { 
      value = null; 
      return false; 
     } 
     value = propertyInfo.GetValue(declaringObject, BindingFlags.GetProperty, null, null, null); 
     return true; 
    } 

    private static bool TryGetFieldValue(object declaringObject, string fieldName, out object value) 
    { 
     var fieldInfo = declaringObject.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 
     if(fieldInfo == null) 
     { 
      value = null; 
      return false; 
     } 
     value = fieldInfo.GetValue(declaringObject); 
     return true; 
    } 
} 

मुझे उम्मीद है कि किसी और की मदद करेगा। अगर कुछ अस्पष्ट है या ऐसा लगता है कि मुझे कुछ याद आया तो सवाल पूछने के लिए स्वतंत्र महसूस करें।

+0

मैं आपको पोस्ट करने की सराहना करता हूं, लेकिन मुझे विश्वास नहीं है कि न्यूटन सॉफ्ट ने इसे इतना जटिल बना दिया है! हमें केवल एक चाहिए चाहिए जो एक मानदंड था जो मूल्य में भी पारित हुआ था, फिर भी कोई प्रतिबिंब आवश्यक नहीं होगा। –

+0

मैंने अभी एक फीचर अनुरोध पोस्ट किया है https://github.com/JamesNK/Newtonsoft.Json/issues/1488 –

4

क्योंकि आप इसे किसी संपत्ति के मूल्य के आधार पर करने के लिए कह रहे हैं जो आप कर सकते हैं डेटा को एक शब्दकोश में डाल दिया जाता है। आप शब्दकोश को मूल्य जोड़ने को बहिष्कृत कर सकते हैं। नीचे एक वस्तु का डेटा प्राप्त करने का एक सरल उदाहरण है।

public class Class1 
{ 
    public string Name { get; set; } 
} 

[TestFixture] 
public class Tests 
{ 
    [Test] 
    public void ConvertTest() 
    { 
     var dictionary = new Dictionary<string, object>(); 
     var @class = new Class1 { Name = "Joe" }; 

     var propertyInfos = typeof (Class1).GetProperties(); 

     foreach (PropertyInfo propertyInfo in propertyInfos) 
     { 
      dictionary.Add(propertyInfo.Name, propertyInfo.GetValue(@class, BindingFlags.GetProperty, null, null, null)); 
     } 

     var serializeObject = JsonConvert.SerializeObject(dictionary); 
     var o = JsonConvert.SerializeObject(@class); 

     Console.WriteLine(serializeObject); 
     Console.WriteLine(o); 

     var class1 = JsonConvert.DeserializeObject<Class1>(serializeObject); 
     Console.WriteLine(class1.Name); 
    } 
} 
+1

तथ्य यह है कि आपका कोड एक इकाई परीक्षण की तरह दिखता है, जिसने मुझे लगभग उत्कृष्ट बिंदु को याद नहीं किया है। मुझे नहीं पता कि यह ओपी प्रश्न को हल करेगा या नहीं, लेकिन मेरे शुरुआती परीक्षणों से पता चलता है कि यह मेरी समस्या को हल करता है जो ओपी के करीब था। –

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