JSON.NET का उपयोग करके क्रमबद्ध क्षेत्रों का क्रम


138

क्या JSON.NET का उपयोग करके क्रमबद्ध JSON ऑब्जेक्ट में फ़ील्ड के आदेश को निर्दिष्ट करने का एक तरीका है ?

यह निर्दिष्ट करना पर्याप्त होगा कि एक एकल क्षेत्र हमेशा पहले दिखाई देता है।


7
मुझे लगता है कि वह शायद पहले आईडी क्षेत्र (या समान) और फिर अन्य सभी क्षेत्रों को दिखाने में रुचि रखते हैं। यह अंतिम-उपयोगकर्ताओं के लिए मित्रवत है, जो कि
A..I से

3
JSON गुणों को अनियंत्रित किया गया है। मुझे लगता है कि क्रमांकन के दौरान किसी विशेष OUTPUT आदेश को लागू करना ठीक है (संभवत: JSON को आईब्रो करने के लिए), लेकिन यह एक बुरा निर्णय होगा कि डिसेर्बलाइजेशन पर किसी विशेष आदेश पर DEPENDENCY बनाया जाए।
डब्लिक

5
वैध कारणों की एक जोड़ी: (1) एक "$ प्रकार" संपत्ति को रोकना जो कि JSON में पहली संपत्ति होनी चाहिए, (2) JSON उत्पन्न करने की कोशिश कर रही है जो यथासंभव संपीड़ित करता है
स्टीफन चुंग

4
एक और कारण (3) एक कैनोनिकल प्रतिनिधित्व हो सकता है जो JSON सिंटैक्स का उपयोग करता है - एक ही ऑब्जेक्ट को एक ही JSON स्ट्रिंग के उत्पादन की गारंटी दी जानी चाहिए। विशेषताओं का एक निर्धारक क्रम इसके लिए एक आवश्यक शर्त है।
मार्कसचैबर

2
केविन, क्या आप इस प्रश्न पर स्वीकृत उत्तर को अपडेट कर सकते हैं?
मिल्ली स्मिथ

जवाबों:


256

समर्थित तरीका JsonPropertyवर्ग गुणों पर उस विशेषता का उपयोग करना है जिसे आप ऑर्डर सेट करना चाहते हैं। अधिक जानकारी के लिए JsonPropertyAttribute आदेश प्रलेखन पढ़ें ।

JsonPropertyएक Orderमान पास करें और बाकी की देखभाल धारावाहिक करेंगे।

 [JsonProperty(Order = 1)]

यह बहुत ही समान है

 DataMember(Order = 1) 

के System.Runtime.Serializationदिन।

यहाँ @ केविन-बेबॉक का एक महत्वपूर्ण नोट है

... ऑर्डर को 1 पर सेट करना केवल तभी काम करेगा जब आप अन्य सभी गुणों पर 1 से अधिक ऑर्डर सेट करेंगे। डिफ़ॉल्ट रूप से ऑर्डर सेटिंग के बिना किसी भी संपत्ति को -1 का ऑर्डर दिया जाएगा। इसलिए आपको या तो सभी क्रमबद्ध संपत्तियों और ऑर्डर देना होगा, या अपना पहला आइटम -2 पर सेट करना होगा


97
कैन की Orderसंपत्ति का JsonPropertyAttributeउपयोग उस क्रम को नियंत्रित करने के लिए किया जा सकता है जिसमें खेतों को क्रमबद्ध / deserialized किया जाता है। हालाँकि, ऑर्डर को 1 पर सेट करना केवल तभी काम करेगा जब आप अन्य सभी गुणों पर 1 से अधिक ऑर्डर सेट करेंगे। डिफ़ॉल्ट रूप से ऑर्डर सेटिंग के बिना किसी भी संपत्ति को -1 का ऑर्डर दिया जाएगा। इसलिए आपको या तो सभी क्रमबद्ध संपत्तियों और ऑर्डर देना होगा, या अपना पहला आइटम -2 पर सेट करना होगा।
केविन बैबॉक

1
यह क्रमबद्धता के लिए काम करता है, लेकिन इस आदेश को deserialization पर विचार नहीं किया जा रहा है। दस्तावेज़ीकरण के अनुसार, क्रम विशेषता का उपयोग क्रमबद्धता और डीरिएलाइज़ेशन दोनों के लिए किया जाता है। क्या आसपास कोई काम है?
cangosta

1
वहाँ के लिए एक समान संपत्ति है JavaScriptSerializer
शिम्मी वेइटहैंडलर

4
@cangosta deserialization का क्रम मायने नहीं रखता है .. सिवाय कुछ बहुत "विषम" अपेक्षा के मामलों में।
user2864740

1
ऑर्डर डिसिएरलाइजेशन में सम्मानित किए जाने की इच्छा के इर्द-गिर्द इसी तरह के गिथब मुद्दों पर चर्चा करें: github.com/JamesNK/Newtonsoft.Json/issues/758 मूल रूप से इस का कोई मौका नहीं है।
टायथ

126

आप वास्तव IContractResolverमें DefaultContractResolver's CreatePropertiesपद्धति को लागू या ओवरराइड करके आदेश को नियंत्रित कर सकते हैं ।

यहाँ मेरे सरल कार्यान्वयन का एक उदाहरण है IContractResolverजो वर्णानुक्रम में गुणों का आदेश देता है:

public class OrderedContractResolver : DefaultContractResolver
{
    protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
    }
}

और फिर सेटिंग्स सेट करें और ऑब्जेक्ट को सीरियल करें, और JSON फ़ील्ड्स वर्णमाला क्रम में होंगे:

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

var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);

11
यह काफी सहायक (+1) है, लेकिन एक चेतावनी: ऐसा प्रतीत होता है कि शब्दकोशों का क्रमांकन इस CreateProperties अनुकूलन का उपयोग नहीं करता है। वे ठीक-ठाक सीरियसली लेते हैं लेकिन सॉर्ट नहीं करते। मुझे लगता है कि शब्दकोशों के क्रमांकन को अनुकूलित करने का एक अलग तरीका है, लेकिन मैंने इसे नहीं पाया है।
घुलनशील मछली

उत्तम। जैसा चाहता था वैसा करता है। धन्यवाद।
वेड हैटलर

यह एक बेहतरीन उपाय है। मेरे लिए विशेष रूप से पूरी तरह से काम किया जब विशेष रूप से 2 JSON ऑब्जेक्ट्स को एक साथ रखकर और गुणों को पंक्तिबद्ध किया।
विंस

16

मेरे मामले में मैटियस का जवाब काम नहीं आया। CreatePropertiesविधि कभी नहीं बुलाया गया था।

Newtonsoft.Jsonइंटर्नल के कुछ डिबगिंग के बाद , मैं एक और समाधान के साथ आया।

public class JsonUtility
{
    public static string NormalizeJsonString(string json)
    {
        // Parse json string into JObject.
        var parsedObject = JObject.Parse(json);

        // Sort properties of JObject.
        var normalizedObject = SortPropertiesAlphabetically(parsedObject);

        // Serialize JObject .
        return JsonConvert.SerializeObject(normalizedObject);
    }

    private static JObject SortPropertiesAlphabetically(JObject original)
    {
        var result = new JObject();

        foreach (var property in original.Properties().ToList().OrderBy(p => p.Name))
        {
            var value = property.Value as JObject;

            if (value != null)
            {
                value = SortPropertiesAlphabetically(value);
                result.Add(property.Name, value);
            }
            else
            {
                result.Add(property.Name, property.Value);
            }
        }

        return result;
    }
}

2
डाइक का उपयोग करते समय यह हमारे लिए आवश्यक फिक्स था।
Noocyte

यह अतिरिक्त deserialization और क्रमांकन के उपरि जोड़ता है। मैंने एक समाधान जोड़ा है जो सामान्य कक्षाओं, शब्दकोशों और एक्सपेंडीओबजेक्ट (गतिशील वस्तु) के लिए भी काम करेगा
जे। शाह

11

मेरे मामले में निहार का समाधान काम नहीं करता था क्योंकि यह सरणियों में वस्तुओं को संभालता नहीं था।

उसके समाधान के आधार पर मैं यही करता आया हूं

public static class JsonUtility
{
    public static string NormalizeJsonString(string json)
    {
        JToken parsed = JToken.Parse(json);

        JToken normalized = NormalizeToken(parsed);

        return JsonConvert.SerializeObject(normalized);
    }

    private static JToken NormalizeToken(JToken token)
    {
        JObject o;
        JArray array;
        if ((o = token as JObject) != null)
        {
            List<JProperty> orderedProperties = new List<JProperty>(o.Properties());
            orderedProperties.Sort(delegate(JProperty x, JProperty y) { return x.Name.CompareTo(y.Name); });
            JObject normalized = new JObject();
            foreach (JProperty property in orderedProperties)
            {
                normalized.Add(property.Name, NormalizeToken(property.Value));
            }
            return normalized;
        }
        else if ((array = token as JArray) != null)
        {
            for (int i = 0; i < array.Count; i++)
            {
                array[i] = NormalizeToken(array[i]);
            }
            return array;
        }
        else
        {
            return token;
        }
    }
}

यह अतिरिक्त deserialization और क्रमांकन के उपरि जोड़ता है।
जय शाह

उत्कृष्ट समाधान। धन्यवाद।
मईया

3

जैसा कि चार्ली ने उल्लेख किया है, आप क्लास में ही प्रॉपर्टीज को ऑर्डर करके JSON प्रॉपर्टीज के ऑर्डर को कुछ हद तक कंट्रोल कर सकते हैं। दुर्भाग्य से, यह दृष्टिकोण एक आधार वर्ग से विरासत में मिली संपत्तियों के लिए काम नहीं करता है। बेस क्लास प्रॉपर्टीज़ को ऑर्डर दिया जाएगा क्योंकि उन्हें कोड में रखा गया है, लेकिन बेस क्लास प्रॉपर्टीज़ के सामने आएंगे।

और किसी को भी आश्चर्य हो रहा है कि आप JSON संपत्तियों की वर्णमाला क्यों करना चाहते हैं, यह कच्चे JSON फ़ाइलों के साथ काम करने के लिए पूरी तरह से आसान है, विशेष रूप से बहुत सारी संपत्तियों के साथ वर्गों के लिए, यदि वे ऑर्डर किए जाते हैं।


2

यह सामान्य कक्षाओं, शब्दकोशों और एक्सपेंडेओबजेक्ट (गतिशील वस्तु) के लिए भी काम करेगा।

class OrderedPropertiesContractResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
        {
            var props = base.CreateProperties(type, memberSerialization);
            return props.OrderBy(p => p.PropertyName).ToList();
        }
    }



class OrderedExpandoPropertiesConverter : ExpandoObjectConverter
    {
        public override bool CanWrite
        {
            get { return true; }
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var expando = (IDictionary<string, object>)value;
            var orderedDictionary = expando.OrderBy(x => x.Key).ToDictionary(t => t.Key, t => t.Value);
            serializer.Serialize(writer, orderedDictionary);
        }
    }



var settings = new JsonSerializerSettings
        {
            ContractResolver = new OrderedPropertiesContractResolver(),
            Converters = { new OrderedExpandoPropertiesConverter() }
        };

var serializedString = JsonConvert.SerializeObject(obj, settings);

क्या यह क्रमांकन के दौरान डिफ़ॉल्ट क्रम व्यवहार नहीं था?
mr5

1
किसी और को कुछ व्यर्थ मिनट बचाने के लिए, ध्यान दें कि यह उत्तर दावे के बावजूद शब्दकोशों के लिए काम नहीं करता है। CreatePropertiesएक शब्दकोश के क्रमांकन के दौरान आह्वान नहीं किया जाता है। मैंने JSON.net रेपो की खोज की कि कौन सी मशीनरी वास्तव में शब्दकोश प्रविष्टियों के माध्यम से धोखा दे रही है। यह overrideआदेश देने के लिए किसी भी या अन्य अनुकूलन में हुक नहीं करता है । यह सिर्फ एंट्रीज़ लेता है जैसा कि ऑब्जेक्ट के एन्यूमरेटर से है। यह मैं एक का निर्माण करने के लिए है लगता है SortedDictionaryया SortedListऐसा करने के लिए मजबूर करने के लिए JSON.net। फ़ीचर सुझाव दायर: github.com/JamesNK/Newtonsoft.Json/issues/2270
विलियम

2

यदि आप JsonProperty Orderहर वर्ग की संपत्ति पर एक विशेषता नहीं रखना चाहते हैं, तो इसका बहुत आसान है कि आप अपना खुद का ContractResolver ...

IContractResolver इंटरफ़ेस JSONSerializer को कैसे अनुकूलित करता है, इसे अनुकूलित करने का एक तरीका प्रदान करता है और JSON को आपकी कक्षाओं में विशेषताओं को रखे बिना .NET ऑब्जेक्ट्स को डिसेर्बलाइज करता है।

ऐशे ही:

private class SortedPropertiesContractResolver : DefaultContractResolver
{
    // use a static instance for optimal performance
    static SortedPropertiesContractResolver instance;

    static SortedPropertiesContractResolver() { instance = new SortedPropertiesContractResolver(); }

    public static SortedPropertiesContractResolver Instance { get { return instance; } }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        if (properties != null)
            return properties.OrderBy(p => p.UnderlyingName).ToList();
        return properties;
    }
}

लागू:

var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);

0

निम्न पुनरावर्ती विधि JObjectएक नई आवृत्ति ऑब्जेक्ट ग्राफ़ बनाने के बजाय मौजूदा टोकन पर आंतरिक टोकन सूची को सॉर्ट करने के लिए प्रतिबिंब का उपयोग करती है । यह कोड आंतरिक Json.NET कार्यान्वयन विवरण पर निर्भर करता है और इसका उपयोग उत्पादन में नहीं किया जाना चाहिए।

void SortProperties(JToken token)
{
    var obj = token as JObject;
    if (obj != null)
    {
        var props = typeof (JObject)
            .GetField("_properties",
                      BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(obj);
        var items = typeof (Collection<JToken>)
            .GetField("items", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(props);
        ArrayList.Adapter((IList) items)
            .Sort(new ComparisonComparer(
                (x, y) =>
                {
                    var xProp = x as JProperty;
                    var yProp = y as JProperty;
                    return xProp != null && yProp != null
                        ? string.Compare(xProp.Name, yProp.Name)
                        : 0;
                }));
    }
    foreach (var child in token.Children())
    {
        SortProperties(child);
    }
}

0

वास्तव में, चूंकि मेरी वस्तु पहले से ही एक जेजबजेक्ट थी, इसलिए मैंने निम्नलिखित समाधान का उपयोग किया:

public class SortedJObject : JObject
{
    public SortedJObject(JObject other)
    {
        var pairs = new List<KeyValuePair<string, JToken>>();
        foreach (var pair in other)
        {
            pairs.Add(pair);
        }
        pairs.OrderBy(p => p.Key).ForEach(pair => this[pair.Key] = pair.Value);
    }
}

और फिर इसे इस तरह उपयोग करें:

string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));

0

यदि आप वर्ग को नियंत्रित करते हैं (अर्थात लिखते हैं), गुणों को वर्णमाला के क्रम में रखते हैं और जब वे वर्णमाला के क्रम में क्रमबद्ध होंगे, जब JsonConvert.SerializeObject()इसे कहा जाता है।


0

मैं एक comblex ऑब्जेक्ट को क्रमबद्ध करना चाहता हूं और गुणों का क्रम जारी रखता हूं क्योंकि वे कोड में परिभाषित किए गए हैं। मैं सिर्फ [JsonProperty(Order = 1)]इसलिए नहीं जोड़ सकता क्योंकि वर्ग मेरे दायरे से बाहर है।

यह समाधान इस बात को भी ध्यान में रखता है कि जिन गुणों को आधार वर्ग में परिभाषित किया गया है, उनकी उच्च प्राथमिकता होनी चाहिए।

यह बुलेटप्रूफ नहीं हो सकता है, क्योंकि कहीं भी परिभाषित नहीं किया गया है कि MetaDataAttributeसही क्रम सुनिश्चित करता है, लेकिन यह काम करने लगता है। मेरे उपयोग के मामले के लिए यह ठीक है। चूँकि मैं केवल ऑटो जनरेटेड कॉन्फ़िगर फ़ाइल के लिए मानव पठनीयता को बनाए रखना चाहता हूँ।

public class PersonWithAge : Person
{
    public int Age { get; set; }
}

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

public string GetJson()
{
    var thequeen = new PersonWithAge { Name = "Elisabeth", Age = Int32.MaxValue };

    var settings = new JsonSerializerSettings()
    {
        ContractResolver = new MetadataTokenContractResolver(),
    };

    return JsonConvert.SerializeObject(
        thequeen, Newtonsoft.Json.Formatting.Indented, settings
    );

}

public class MetadataTokenContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(
        Type type, MemberSerialization memberSerialization)
    {
        var props = type
           .GetProperties(BindingFlags.Instance
               | BindingFlags.Public
               | BindingFlags.NonPublic
           ).ToDictionary(k => k.Name, v =>
           {
               // first value: declaring type
               var classIndex = 0;
               var t = type;
               while (t != v.DeclaringType)
               {
                   classIndex++;
                   t = type.BaseType;
               }
               return Tuple.Create(classIndex, v.MetadataToken);
           });

        return base.CreateProperties(type, memberSerialization)
            .OrderByDescending(p => props[p.PropertyName].Item1)
            .ThenBy(p => props[p.PropertyName].Item1)
            .ToList();
    }
}


-1

यदि आप विश्व स्तर पर अपने एपीआई को ऑर्डर किए गए फ़ील्ड से कॉन्फ़िगर करना चाहते हैं, तो कृपया मैटियस नॉर्डबर्ग उत्तर को संयोजित करें:

public class OrderedContractResolver : DefaultContractResolver
{
    protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
    }
}

यहाँ मेरे जवाब के साथ:

ASP.NET वेब एपीआई को हमेशा JSON वापस करने के लिए कैसे मजबूर करें?


-5

अपडेट करें

मैंने बस नीचे की ओर देखा। ऐसा करने के लिए नीचे 'स्टीव' से जवाब देखें।

मूल

मैंने JsonConvert.SerializeObject(key)प्रतिबिंब के माध्यम से विधि कॉल का पालन ​​किया (जहां कुंजी एक IList थी) और पाया कि JsonSerializerInternalWriter.SerializeList कहा जाता है। यह एक सूची लेता है और के माध्यम से छोरों

for (int i = 0; i < values.Count; i++) { ...

मूल्यों में IList पैरामीटर कहाँ लाया जाता है।

संक्षिप्त उत्तर है ... नहीं, फ़ील्ड को JSON स्ट्रिंग में सूचीबद्ध करने के क्रम को सेट करने के लिए कोई तरीका नहीं बनाया गया है।


18
संक्षिप्त उत्तर, लेकिन संभवतः पुराना। स्टीव के उत्तर की जाँच करें (जेम्स न्यूटन-किंग द्वारा समर्थित)
ब्रैड ब्रूस

-6

JSON प्रारूप में फ़ील्ड का कोई क्रम नहीं है, इसलिए किसी आदेश को परिभाषित करने का कोई मतलब नहीं है।

{ id: 1, name: 'John' }के बराबर है { name: 'John', id: 1 }(दोनों एक कड़ाई से समकक्ष वस्तु उदाहरण का प्रतिनिधित्व करते हैं)


12
@ डारिन - लेकिन क्रमबद्धता में एक आदेश है। {{id: 1, नाम: 'जॉन'} और "{नाम: 'जॉन', आईडी: 1}" तार के रूप में अलग-अलग हैं , जो कि मुझे यहां की परवाह है। निश्चित रूप से, वस्तुएं जब deserialized के बराबर होती हैं।
केविन मोंट्रोस

1
@ डारिन - नहीं, इस मामले में नहीं। मैं कुछ सीरियल कर रहा हूं और फिर इसे एक स्ट्रिंग के रूप में एक सेवा के रूप में पारित कर रहा हूं जो केवल स्ट्रिंग्स में काम करता है (JSON जागरूक नहीं), और यह स्ट्रिंग में पहली बार दिखाई देने के लिए एक क्षेत्र के विभिन्न कारणों के लिए सुविधाजनक होगा।
केविन मॉन्ट्रोस

1
यह भी परीक्षण के लिए अच्छा है, बस deserialize करने के बजाय तार को देखने के लिए सक्षम होने के नाते।
स्टीव

9
एक स्थिर क्रमांकन आदेश कैश सत्यापन के लिए भी आसान है। यह एक स्ट्रिंग का चेकसम लेने के लिए तुच्छ है - एक पूर्ण वस्तु ग्राफ का सच नहीं।
घुलनशील मछली

1
इकाई परीक्षण करते समय सीरियलाइज़ेशन ऑर्डर भी आसान है ताकि आप आसानी से कह सकें कि अपेक्षित बनाम वास्तविक प्रतिक्रिया स्ट्रिंग्स समान हैं, जब भी जॉन्स गुण अलग-अलग होते हैं।
अनॉन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.