प्रॉपर्टी डिसेरिएलाइज़ करना लेकिन json.net के साथ सीरीज़ नहीं करना


124

हमारे पास कुछ विन्यास फाइलें हैं जो Json.net के साथ C # ऑब्जेक्ट्स को क्रमांकित करके उत्पन्न की गई थीं।

हम क्रमबद्ध वर्ग की एक संपत्ति को एक साधारण संपत्ति से एक वर्ग संपत्ति में स्थानांतरित करना चाहते हैं।

ऐसा करने का एक आसान तरीका है, पुरानी एनम प्रॉपर्टी को क्लास पर छोड़ना, और JSON.net द्वारा इस प्रॉपर्टी को पढ़ने के लिए व्यवस्थित करना, जब हम कॉन्फिगरेशन लोड करते हैं, लेकिन इसे फिर से सेव करने के लिए नहीं जब हम अगली बार ऑब्जेक्ट को सीरियल करते हैं। हम पुराने एनम से अलग से नई कक्षा उत्पन्न करने से निपटेंगे।

क्या सी # ऑब्जेक्ट की एक संपत्ति (जैसे विशेषताओं के साथ) को चिह्नित करने का कोई सरल तरीका है, ताकि Json.net इसे केवल सीरियलाइज़ करते समय अनदेखा कर दे, लेकिन डिसेरिएलाइज़ करते समय इसमें शामिल हों?


एक कस्टम कनवर्टर के बारे में क्या: आप इसे अपनी संपत्ति पर एक विशेषता के रूप में उपयोग कर सकते हैं, ReadJson और WriteJson को विभिन्न घटकों के साथ ओवरराइड कर सकते हैं, नहीं? उदाहरण (नहीं आप वास्तव में क्या जरूरत है, लेकिन ...) weblogs.asp.net/thangchung/archive/2010/08/26/...
Raphaël Althaus

OnDeserialized विशेषता आपके लिए एक काम के इर्द-गिर्द हो सकती है
एस्टेफनी वेलेज़

यह `[JsonIgnore] 'विशेषता का उपयोग कर संभव नहीं होना चाहिए ?? james.newtonking.com/archive/2009/10/23/…
जूरी

क्या आप इस बात का विस्तार करने में सक्षम हैं कि q के अंतिम पैराग्राफ के अनुसार इसका उपयोग केवल एक दिशा में कैसे किया जा सकता है?
विल डीन

[JsonIgnore] को उस [JsonProperty] विशेषता से सजाया गया है जो एक माध्यमिक निजी सेटर के संयोजन में उपयोग करना संभव है। वहाँ कुछ अन्य सरल समाधान के रूप में अच्छी तरह से कर रहे हैं। मैंने एक विस्तृत राइटअप जोड़ा है।
ब्रायन रोजर्स

जवाबों:


133

वास्तव में कई सरल दृष्टिकोण हैं जिनका उपयोग आप इच्छित परिणाम प्राप्त करने के लिए कर सकते हैं।

उदाहरण के लिए, मान लें कि आपके पास वर्तमान में इस तरह परिभाषित आपकी कक्षाएं हैं:

class Config
{
    public Fizz ObsoleteSetting { get; set; }
    public Bang ReplacementSetting { get; set; }
}

enum Fizz { Alpha, Beta, Gamma }

class Bang
{
    public string Value { get; set; }
}

और आप यह करना चाहते हैं:

string json = @"{ ""ObsoleteSetting"" : ""Gamma"" }";

// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);

// migrate
config.ReplacementSetting = 
    new Bang { Value = config.ObsoleteSetting.ToString() };

// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);

इसे पाने के लिए:

{"ReplacementSetting":{"Value":"Gamma"}}

दृष्टिकोण 1: एक कंधे विधि जोड़ें

Json.NET में ShouldSerializeक्लास में संबंधित तरीकों की तलाश करके गुणों को सशर्त रूप से अनुक्रमित करने की क्षमता है ।

इस सुविधा का उपयोग करने के लिए, ShouldSerializeBlah()अपनी कक्षा में एक बूलियन पद्धति जोड़ें जहां Blahउस संपत्ति के नाम से प्रतिस्थापित किया जाता है जिसे आप क्रमबद्ध नहीं करना चाहते हैं। इस पद्धति का कार्यान्वयन हमेशा वापस करें false

class Config
{
    public Fizz ObsoleteSetting { get; set; }

    public Bang ReplacementSetting { get; set; }

    public bool ShouldSerializeObsoleteSetting()
    {
        return false;
    }
}

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

दृष्टिकोण 2: जॉन्स को जोबजेक्ट्स के साथ जोड़ दें

JsonConvert.SerializeObjectक्रमांकन करने के लिए उपयोग करने के बजाय , कॉन्फ़िगर ऑब्जेक्ट को एक में लोड करें JObject, फिर इसे लिखने से पहले JSON से अवांछित संपत्ति को हटा दें। यह कोड की कुछ अतिरिक्त पंक्तियाँ हैं।

JObject jo = JObject.FromObject(config);

// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();

json = jo.ToString();

दृष्टिकोण 3: विशेषताओं का चतुर (ab) उपयोग

  1. उस [JsonIgnore]गुण पर लागू करें जिसे आप क्रमबद्ध नहीं करना चाहते हैं।
  2. मूल संपत्ति के समान वर्ग के साथ एक वैकल्पिक, निजी संपत्ति सेटर जोड़ें । उस संपत्ति के कार्यान्वयन को मूल संपत्ति निर्धारित करें।
  3. [JsonProperty]वैकल्पिक सेटर के लिए एक विशेषता लागू करें , इसे मूल संपत्ति के समान JSON नाम दें।

यहाँ संशोधित Configवर्ग है:

class Config
{
    [JsonIgnore]
    public Fizz ObsoleteSetting { get; set; }

    [JsonProperty("ObsoleteSetting")]
    private Fizz ObsoleteSettingAlternateSetter
    {
        // get is intentionally omitted here
        set { ObsoleteSetting = value; }
    }

    public Bang ReplacementSetting { get; set; }
}

7
हमने इसे अपनी परियोजना में हल किया (जो बेस मॉडल के आंतरिक एकीकरण-विशिष्ट सुपर सेट का उपयोग करता है, जहां सुपरक्लास गुणों में से कोई भी अनुक्रमित नहीं होना चाहिए), गेट-गुण को आंतरिक में सेट करके। सार्वजनिक बसने वालों ने वेब एप को संपत्तियों को सेट करने की अनुमति दी, लेकिन इसे उन्हें सीरियल करने से रोक दिया।
डेनियल सईदी

7
JsonPropertyAttributeC # 6.0 के उपयोग के साथ संयोजन में , आप nameof"मैजिक स्ट्रिंग्स" का उपयोग करने के बजाय कीवर्ड का उपयोग कर सकते हैं । यह बहुत आसान और मूर्खतापूर्ण सबूत को परिष्कृत करता है - इसके अलावा, यदि आप किसी भी घटना का नाम बदलने से चूक जाते हैं, तो संकलक आपको चेतावनी देगा, वैसे भी। @ ब्रायन के उदाहरण का उपयोग करते हुए, यह इस प्रकार होगा:[JsonProperty(nameof(ObsoleteSetting))]
ज्योफ जेम्स

1
JsonProperty घोषणाओं में nameof () का उपयोग करना एक बुरा विचार है, विशेष रूप से इस विरासत परिदृश्य में। JSON एक अन्य इंटरफ़ेस के साथ एक बाहरी (और उम्मीद है कि शाश्वत) अनुबंध का प्रतिनिधित्व करता है, और आप निश्चित रूप से JSON संपत्ति का नाम बदलना नहीं चाहते हैं यदि आप रिफ्लेक्टर करते हैं। आप सभी मौजूदा JSON फ़ाइलों और घटकों को तोड़ रहे होंगे जो JSON को इस प्रारूप में उत्पन्न करते हैं। वास्तव में, आप JsonProperty (…) को प्रत्येक क्रमबद्ध संपत्ति पर पूरे नाम के साथ रखना सुनिश्चित करेंगे, यदि आप बाद में संपत्ति का नाम बदलते हैं तो वे नहीं बदलते हैं।
अम्मो गोएत्सच

36

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

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

यह डिफ़ॉल्ट सेटिंग्स / रिज़ॉल्वर / आदि का उपयोग करके सही डीरिएरलाइज़ेशन में परिणाम करता है, लेकिन संपत्ति क्रमबद्ध आउटपुट से छीन ली गई है।


सरल अभी तक चतुर समाधान।
hbulens

ध्यान दें कि संपत्ति को सत्यापन मॉड्यूल द्वारा भी अनदेखा किया जाएगा। (इसलिए आप इसे अब आवश्यक नहीं है, क्योंकि यह एक सार्वजनिक getविधि पर निर्भर है )।
मार्टिन हैनसेन

2
यह न तो साथ काम करता है internalऔर न ही private। यह हमेशा क्रमबद्ध होता है।
पॉल 15

इसने मेरे लिए काम नहीं किया। डीसर्लाइज़ करते समय कोई संपत्ति नहीं मिली।
सुमीडो

31

मैं इस एक पर विशेषताओं के साथ चिपके रहना पसंद करता हूं, यहां वह विधि है जिसका उपयोग मैं किसी संपत्ति को निष्क्रिय करने के लिए करता हूं, लेकिन इसे क्रमबद्ध या इसके विपरीत नहीं करता हूं।

STEP 1 - कस्टम विशेषता बनाएं

public class JsonIgnoreSerializationAttribute : Attribute { }

STEP 2 - एक कस्टम कॉन्ट्रैक्ट रिज़लवर बनाएं

class JsonPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        //Return properties that do NOT have the JsonIgnoreSerializationAttribute
        return objectType.GetProperties()
                         .Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
                         .ToList<MemberInfo>();
    }
}

चरण 3 - वह विशेषता जोड़ें जहां क्रमांकन की आवश्यकता नहीं है लेकिन deserialization है

    [JsonIgnoreSerialization]
    public string Prop1 { get; set; } //Will be skipped when serialized

    [JsonIgnoreSerialization]
    public string Prop2 { get; set; } //Also will be skipped when serialized

    public string Prop3 { get; set; } //Will not be skipped when serialized

चरण 4 - इसका उपयोग करें

var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });

उम्मीद है की यह मदद करेगा! यह भी ध्यान देने योग्य है कि यह गुणों की उपेक्षा भी करेगा जब Deserialization होता है, जब मैं derserializing हूं मैं पारंपरिक तरीके से कनवर्टर का उपयोग करता हूं।

JsonConvert.DeserializeObject<MyType>(myString);

इस उपयोगी कार्यान्वयन के लिए धन्यवाद। क्या GetSerializableMembersइसे पूरी तरह से ओवरराइड करने के बजाय आधार कार्यान्वयन का विस्तार करने का एक तरीका है ?
एलेन

4
कोई बात नहीं, बस एहसास हुआ कि यह उतना ही सरल था:return base.GetSerializableMembers(objectType).Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList();
Alain

यकीन नहीं होता कि यह टॉप-रेटेड उत्तर क्यों नहीं है। यह साफ है, न्यूटनसॉफ्ट के पैटर्न का अनुसरण करता है और यह करना आसान है। केवल एक चीज जो मैं JsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() }
मैट एम

1
कोई बात नहीं, यह वास्तव में वह नहीं करता है जो पूछने वाला पूछ रहा है। यह मूल रूप से JsonIgnore को फिर से बना रहा है। यह संपत्ति को क्रमांकन के लिए नहीं छोड़ता है, लेकिन संपत्ति को डीरियलाइज़ेशन के दौरान सेट करता है क्योंकि GetSerializableMembers विधि के लिए कोई रास्ता नहीं है यह जानने के लिए कि क्या यह पढ़ा या लिखा है इसलिए आप दोनों के लिए गुणों को बाहर कर रहे हैं। यह समाधान काम नहीं करता है।
मैट एम

7

सेटर प्रॉपर्टी का उपयोग करें:

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }

[JsonIgnore]
private string _ignoreOnSerializing;

[JsonIgnore]
public string IgnoreOnSerializing
{
    get { return this._ignoreOnSerializing; }
    set { this._ignoreOnSerializing = value; }
}

उममीद है कि इससे मदद मिलेगी।


1
धन्यवाद। ध्यान दें कि IgnoreOnSerializingसंपत्ति के बराबर JsonProperty का अपरकेस होना चाहिए । मैं nameof(IgnoreOnSerializing)नाम बदलने के मामले में, मैजिक स्ट्रिंग से बचने के लिए उपयोग करने की सलाह देता हूं ।
बेंडिक अगस्त नेस्ब्रो

5

जब मैंने एक लंबे समय तक खोज करने के बाद एक वर्ग की संपत्ति को डी-सेरिसेबल करने के लिए ध्वजांकित किया और न ही सीरियल करने योग्य पाया, तो मैंने पाया कि ऐसा करने के लिए कोई ऐसी चीज नहीं है; इसलिए मैं एक समाधान के साथ आया जो दो अलग-अलग पुस्तकालयों या क्रमबद्ध तकनीकों को जोड़ता है (System.Runtime.Serialization.Json & Newtonsoft.Json) और यह मेरे लिए निम्नलिखित की तरह काम करता है:

  • "DataContract" के रूप में अपने सभी वर्ग और उप-वर्गों को चिह्नित करें।
  • "DataMember" के रूप में अपनी कक्षा और उप-वर्गों की सभी संपत्तियों को चिह्नित करें।
  • अपनी कक्षा और उप-वर्गों की सभी संपत्तियों को "JsonProperty" के रूप में चिह्नित करें, सिवाय इसके कि आप उन्हें क्रमबद्ध नहीं करना चाहते हैं।
  • अब उन गुणों को चिह्नित करें जिन्हें आप नहीं चाहते कि इसे "JsonIgnore" के रूप में क्रमांकित किया जाए।
  • तब "Newtonsoft.Json.JsonConvert.SerializeObject" और De-Serialize का उपयोग करके "System.Runtime.Serialization.Json.DataContractJsonSerializer" का उपयोग करके सीरियल करें।

    using System;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;
    
    namespace LUM_Win.model
    {
        [DataContract]
        public class User
        {
            public User() { }
            public User(String JSONObject)
            {
                MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
                DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
    
                User user = (User)dataContractJsonSerializer.ReadObject(stream);
                this.ID = user.ID;
                this.Country = user.Country;
                this.FirstName = user.FirstName;
                this.LastName = user.LastName;
                this.Nickname = user.Nickname;
                this.PhoneNumber = user.PhoneNumber;
                this.DisplayPicture = user.DisplayPicture;
                this.IsRegistred = user.IsRegistred;
                this.IsConfirmed = user.IsConfirmed;
                this.VerificationCode = user.VerificationCode;
                this.Meetings = user.Meetings;
            }
    
            [DataMember(Name = "_id")]
            [JsonProperty(PropertyName = "_id")]
            public String ID { get; set; }
    
            [DataMember(Name = "country")]
            [JsonProperty(PropertyName = "country")]
            public String Country { get; set; }
    
            [DataMember(Name = "firstname")]
            [JsonProperty(PropertyName = "firstname")]
            public String FirstName { get; set; }
    
            [DataMember(Name = "lastname")]
            [JsonProperty(PropertyName = "lastname")]
            public String LastName { get; set; }
    
            [DataMember(Name = "nickname")]
            [JsonProperty(PropertyName = "nickname")]
            public String Nickname { get; set; }
    
            [DataMember(Name = "number")]
            [JsonProperty(PropertyName = "number")]
            public String PhoneNumber { get; set; }
    
            [DataMember(Name = "thumbnail")]
            [JsonProperty(PropertyName = "thumbnail")]
            public String DisplayPicture { get; set; }
    
            [DataMember(Name = "registered")]
            [JsonProperty(PropertyName = "registered")]
            public bool IsRegistred { get; set; }
    
            [DataMember(Name = "confirmed")]
            [JsonProperty(PropertyName = "confirmed")]
            public bool IsConfirmed { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "verification_code")]
            public String VerificationCode { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "meeting_ids")]
            public List<Meeting> Meetings { get; set; }
    
            public String toJSONString()
            {
                return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            }
        }
    }

उम्मीद है की वो मदद करदे ...


1
ब्रावो अहमद अबुलज़म। धन्यवाद, इसने मुझे बहुत काम से बचाया। :)
सइक १२

2

@ थोहो के समाधान के संदर्भ में, सेटर का उपयोग करना वास्तव में सभी की जरूरत है, जिसमें कोई अतिरिक्त टैग नहीं है।

मेरे लिए मेरे पास पहले से एक संदर्भ आईडी थी, जिसे मैं संदर्भ आईडी के नए संग्रह में लोड और जोड़ना चाहता था। संदर्भ आईडी की परिभाषा को बदलकर केवल एक सेटर विधि होती है, जिसने मूल्य को नए संग्रह में जोड़ा। यदि संपत्ति नहीं मिलती है, तो Json मान वापस नहीं लिख सकता है; तरीका।

// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }

// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }

यह वर्ग अब पिछले संस्करण के साथ संगत है और केवल नए संस्करणों के लिए RefIds बचाता है ।


1

थो हो के उत्तर का निर्माण करने के लिए, इसका उपयोग खेतों के लिए भी किया जा सकता है।

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }

[JsonIgnore]
public string IgnoreOnSerializing;

1

इस बात पर निर्भर करता है कि आवेदन में कहां जगह है और यदि यह सिर्फ एक संपत्ति है, तो एक मैनुअल तरीका जिससे आप ऐसा कर सकते हैं, संपत्ति के मूल्य को शून्य पर सेट करके और फिर उस मॉडल पर आप यह निर्दिष्ट कर सकते हैं कि यदि मूल्य शून्य है तो संपत्ति को नजरअंदाज किया जा सकता है:

[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }

यदि आप ASP.NET कोर वेब ऐप पर काम कर रहे हैं, तो आप इसे अपने Startup.cs फ़ाइल में सेट करके सभी मॉडलों के सभी गुणों के लिए वैश्विक रूप से सेट कर सकते हैं:

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

0

यदि आप JsonConvert का उपयोग करते हैं, तो IgnoreDataMemberAttribute ठीक है। मेरी मानक लाइब्रेरी न्यूटन को नहीं दर्शाती है। Json, और मैं ऑब्जेक्ट सीरीज़ को नियंत्रित करने के लिए [IgnoreDataMember] का उपयोग करता हूं।

से Newton.net मदद दस्तावेज़।


0

क्या सी # ऑब्जेक्ट की एक संपत्ति (जैसे विशेषताओं के साथ) को चिह्नित करने का कोई सरल तरीका है, ताकि Json.net इसे केवल सीरियलाइज़ करते समय अनदेखा कर दे, लेकिन डिसेरिएलाइज़ करते समय इसमें शामिल हों?

इस लेखन के रूप में मैंने जो सबसे आसान तरीका पाया है वह इस तर्क को अपने IContractResolver में शामिल करना है

उपर दिए गए लिंक से नमूना कोड पोस्टरिटी के लिए यहां कॉपी किया गया है:

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

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

        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e;
                };
        }

        return property;
    }
}

सभी उत्तर अच्छे हैं लेकिन यह दृष्टिकोण सबसे साफ तरीका लग रहा था। मैंने वास्तव में SkipSerialize और SkipDeserialize के लिए संपत्ति पर एक विशेषता की तलाश करके इसे लागू किया ताकि आप अपने नियंत्रण वाले किसी भी वर्ग को चिह्नित कर सकें। बड़ा अच्छा सवाल!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.