2012-01-12 22 views
45

में अशक्त Serializing जब JSON.NET, किसी भी संपत्ति है कि रिक्तJSON.NET

रूप

"PROPERTYNAME" JSON करने के लिए लिखा है के माध्यम से मनमाने ढंग से डेटा serializing: अशक्त

यह सही है, बेशक।

हालांकि मुझे सभी खाली नलिकाओं को डिफ़ॉल्ट खाली मान में स्वचालित रूप से अनुवाद करने की आवश्यकता है, उदा। शून्य string एस String.Empty बनना चाहिए, शून्य int? एस 0 बनना चाहिए, शून्य bool? एस false होना चाहिए, और इसी तरह।

NullValueHandling सहायक नहीं है, क्योंकि मैं Ignore नल नहीं चाहता, लेकिन न तो मैं Include उन्हें (हम्म, नई सुविधा?) चाहता हूं।

तो मैं एक कस्टम JsonConverter लागू करने के लिए बदल गया।
जबकि कार्यान्वयन स्वयं एक हवा था, दुर्भाग्यवश यह अभी भी काम नहीं करता - CanConvert() कभी ऐसी संपत्ति के लिए नहीं कहा जाता है जिसमें शून्य मान होता है, और इसलिए WriteJson() को भी नहीं कहा जाता है। स्पष्ट रूप से नल स्वचालित रूप से कस्टम पाइपलाइन के बिना सीधे null में क्रमबद्ध कर दिए जाते हैं।

उदाहरण के लिए, यहाँ अशक्त तार के लिए एक कस्टम कनवर्टर का एक नमूना है:

public class StringConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(string).IsAssignableFrom(objectType); 
    } 

    ... 
    public override void WriteJson(JsonWriter writer, 
       object value, 
       JsonSerializer serializer) 
    { 
     string strValue = value as string; 

     if (strValue == null) 
     { 
      writer.WriteValue(String.Empty); 
     } 
     else 
     { 
      writer.WriteValue(strValue); 
     } 
    } 
} 

डीबगर में इस के माध्यम से कदम, मैं ने कहा कि इन तरीकों में से न तो गुण एक शून्य मान है कि के लिए कहा जाता है।

JSON.NET के स्रोत कोड में डेलविंग, मैंने पाया कि (जाहिर है, मैं बहुत गहराई में नहीं गया) नल के लिए एक विशेष मामला जांच रहा है, और स्पष्ट रूप से .WriteNull() पर कॉल कर रहा है।

क्या इसके लायक है के लिए, मैंने किया था एक कस्टम JsonTextWriter लागू करने और डिफ़ॉल्ट .WriteNull() कार्यान्वयन अधिरोहण करने का प्रयास ...

public class NullJsonWriter : JsonTextWriter 
{ 
    ... 
    public override void WriteNull() 
    { 
     this.WriteValue(String.Empty); 
    } 
} 

बहरहाल, यह अच्छी तरह से काम नहीं कर सकते हैं के बाद से WriteNull() विधि अंतर्निहित के बारे में कुछ नहीं जानता डाटा प्रकार। तो निश्चित है, मैं किसी भी शून्य के लिए "" आउटपुट कर सकता हूं, लेकिन यह ठीक से काम नहीं करता है int, bool, आदि

तो, मेरा प्रश्न - पूरी डेटा संरचना को मैन्युअल रूप से परिवर्तित करने से कम, क्या इसके लिए कोई समाधान या समाधान है?

+0

मैं अनुमान लगा रहा हूं कि 'WriteNull() 'विधि को JSON क्रमबद्धता प्रक्रिया के भीतर आंतरिक रूप से बुलाया जाता है और आप यह निर्धारित नहीं कर सकते कि आप वर्तमान में कौन से मूल्य को क्रमबद्ध कर रहे हैं? –

+0

जब लिखित नल विधि को JsonSerializer द्वारा बुलाया जाता है तो संपत्ति का शून्य मान होता है। सटीक होने के लिए, मैं जिस मान को क्रमबद्ध कर रहा हूं वह हमेशा शून्य है :), लेकिन हां अंतर्निहित डेटा प्रकार को जानने का कोई तरीका नहीं है जिसके लिए नल लिखा जा रहा है। – AviD

+0

यदि आप ऑब्जेक्ट की वैध स्थिति के रूप में शून्य को अनदेखा करने जा रहे हैं तो निरर्थक प्रकारों का उपयोग करने का क्या मतलब है? –

उत्तर

25

ठीक है, मुझे लगता है कि मैं एक समाधान के साथ आया हूं (मेरा पहला समाधान बिल्कुल सही नहीं था, लेकिन फिर मैं ट्रेन पर था)। आपको एक विशेष अनुबंध रिज़ॉल्वर और निरर्थक प्रकारों के लिए एक कस्टम ValueProvider बनाने की आवश्यकता है। इस पर विचार करें:

public class NullableValueProvider : IValueProvider 
{ 
    private readonly object _defaultValue; 
    private readonly IValueProvider _underlyingValueProvider; 


    public NullableValueProvider(MemberInfo memberInfo, Type underlyingType) 
    { 
     _underlyingValueProvider = new DynamicValueProvider(memberInfo); 
     _defaultValue = Activator.CreateInstance(underlyingType); 
    } 

    public void SetValue(object target, object value) 
    { 
     _underlyingValueProvider.SetValue(target, value); 
    } 

    public object GetValue(object target) 
    { 
     return _underlyingValueProvider.GetValue(target) ?? _defaultValue; 
    } 
} 

public class SpecialContractResolver : DefaultContractResolver 
{ 
    protected override IValueProvider CreateMemberValueProvider(MemberInfo member) 
    { 
     if(member.MemberType == MemberTypes.Property) 
     { 
      var pi = (PropertyInfo) member; 
      if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof (Nullable<>)) 
      { 
       return new NullableValueProvider(member, pi.PropertyType.GetGenericArguments().First()); 
      } 
     } 
     else if(member.MemberType == MemberTypes.Field) 
     { 
      var fi = (FieldInfo) member; 
      if(fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>)) 
       return new NullableValueProvider(member, fi.FieldType.GetGenericArguments().First()); 
     } 

     return base.CreateMemberValueProvider(member); 
    } 
} 

तो मैं इसे प्रयोग का परीक्षण किया:

class Foo 
{ 
    public int? Int { get; set; } 
    public bool? Boolean { get; set; } 
    public int? IntField; 
} 

और निम्नलिखित मामला:

[TestFixture] 
public class Tests 
{ 
    [Test] 
    public void Test() 
    { 
     var foo = new Foo(); 

     var settings = new JsonSerializerSettings { ContractResolver = new SpecialContractResolver() }; 

     Assert.AreEqual(
      JsonConvert.SerializeObject(foo, Formatting.None, settings), 
      "{\"IntField\":0,\"Int\":0,\"Boolean\":false}"); 
    } 
} 

उम्मीद है कि इस मदद करता है एक सा ...

संपादित – एक Nullable<> प्रकार की बेहतर पहचान

संपादित क्षेत्रों के साथ-साथ गुण, सामान्य DynamicValueProvider अधिकांश काम करने की चोटी पर भी पिग्गी-समर्थन, अद्यतन के साथ के लिए – जोड़ा समर्थन परीक्षण

+0

मुझे विश्वास है कि आप टाइप कर सकते हैं। IValueType यदि आप चाहते हैं। –

+0

@IanJacobs मैंने इसे 'GetGenericTypeDefinition() == टाइपऑफ (Nullable <>) ' –

+0

वाह का उपयोग करके समझ लिया, यह है ... यह अपेक्षाकृत थोड़ा अधिक जटिल है। विशेष रूप से कुछ ऐसा करने के लिए ... वैसे भी यह ले जाएगा मुझे प्लग इन करने और जांचने में थोड़ी देर लगती है, लेकिन यह अच्छा लग रहा है! धन्यवाद, इस बीच ... – AviD