क्या JSON.NET का उपयोग करके क्रमबद्ध JSON ऑब्जेक्ट में फ़ील्ड के आदेश को निर्दिष्ट करने का एक तरीका है ?
यह निर्दिष्ट करना पर्याप्त होगा कि एक एकल क्षेत्र हमेशा पहले दिखाई देता है।
क्या JSON.NET का उपयोग करके क्रमबद्ध JSON ऑब्जेक्ट में फ़ील्ड के आदेश को निर्दिष्ट करने का एक तरीका है ?
यह निर्दिष्ट करना पर्याप्त होगा कि एक एकल क्षेत्र हमेशा पहले दिखाई देता है।
जवाबों:
समर्थित तरीका JsonPropertyवर्ग गुणों पर उस विशेषता का उपयोग करना है जिसे आप ऑर्डर सेट करना चाहते हैं। अधिक जानकारी के लिए JsonPropertyAttribute आदेश प्रलेखन पढ़ें ।
JsonPropertyएक Orderमान पास करें और बाकी की देखभाल धारावाहिक करेंगे।
[JsonProperty(Order = 1)]
यह बहुत ही समान है
DataMember(Order = 1)
के System.Runtime.Serializationदिन।
यहाँ @ केविन-बेबॉक का एक महत्वपूर्ण नोट है
... ऑर्डर को 1 पर सेट करना केवल तभी काम करेगा जब आप अन्य सभी गुणों पर 1 से अधिक ऑर्डर सेट करेंगे। डिफ़ॉल्ट रूप से ऑर्डर सेटिंग के बिना किसी भी संपत्ति को -1 का ऑर्डर दिया जाएगा। इसलिए आपको या तो सभी क्रमबद्ध संपत्तियों और ऑर्डर देना होगा, या अपना पहला आइटम -2 पर सेट करना होगा
Orderसंपत्ति का JsonPropertyAttributeउपयोग उस क्रम को नियंत्रित करने के लिए किया जा सकता है जिसमें खेतों को क्रमबद्ध / deserialized किया जाता है। हालाँकि, ऑर्डर को 1 पर सेट करना केवल तभी काम करेगा जब आप अन्य सभी गुणों पर 1 से अधिक ऑर्डर सेट करेंगे। डिफ़ॉल्ट रूप से ऑर्डर सेटिंग के बिना किसी भी संपत्ति को -1 का ऑर्डर दिया जाएगा। इसलिए आपको या तो सभी क्रमबद्ध संपत्तियों और ऑर्डर देना होगा, या अपना पहला आइटम -2 पर सेट करना होगा।
JavaScriptSerializer।
आप वास्तव 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);
मेरे मामले में मैटियस का जवाब काम नहीं आया। 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;
}
}
मेरे मामले में निहार का समाधान काम नहीं करता था क्योंकि यह सरणियों में वस्तुओं को संभालता नहीं था।
उसके समाधान के आधार पर मैं यही करता आया हूं
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;
}
}
}
जैसा कि चार्ली ने उल्लेख किया है, आप क्लास में ही प्रॉपर्टीज को ऑर्डर करके JSON प्रॉपर्टीज के ऑर्डर को कुछ हद तक कंट्रोल कर सकते हैं। दुर्भाग्य से, यह दृष्टिकोण एक आधार वर्ग से विरासत में मिली संपत्तियों के लिए काम नहीं करता है। बेस क्लास प्रॉपर्टीज़ को ऑर्डर दिया जाएगा क्योंकि उन्हें कोड में रखा गया है, लेकिन बेस क्लास प्रॉपर्टीज़ के सामने आएंगे।
और किसी को भी आश्चर्य हो रहा है कि आप JSON संपत्तियों की वर्णमाला क्यों करना चाहते हैं, यह कच्चे JSON फ़ाइलों के साथ काम करने के लिए पूरी तरह से आसान है, विशेष रूप से बहुत सारी संपत्तियों के साथ वर्गों के लिए, यदि वे ऑर्डर किए जाते हैं।
यह सामान्य कक्षाओं, शब्दकोशों और एक्सपेंडेओबजेक्ट (गतिशील वस्तु) के लिए भी काम करेगा।
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);
CreatePropertiesएक शब्दकोश के क्रमांकन के दौरान आह्वान नहीं किया जाता है। मैंने JSON.net रेपो की खोज की कि कौन सी मशीनरी वास्तव में शब्दकोश प्रविष्टियों के माध्यम से धोखा दे रही है। यह overrideआदेश देने के लिए किसी भी या अन्य अनुकूलन में हुक नहीं करता है । यह सिर्फ एंट्रीज़ लेता है जैसा कि ऑब्जेक्ट के एन्यूमरेटर से है। यह मैं एक का निर्माण करने के लिए है लगता है SortedDictionaryया SortedListऐसा करने के लिए मजबूर करने के लिए JSON.net। फ़ीचर सुझाव दायर: github.com/JamesNK/Newtonsoft.Json/issues/2270
यदि आप 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);
निम्न पुनरावर्ती विधि 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);
}
}
वास्तव में, चूंकि मेरी वस्तु पहले से ही एक जेजबजेक्ट थी, इसलिए मैंने निम्नलिखित समाधान का उपयोग किया:
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));
मैं एक 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();
}
}
यदि आप विश्व स्तर पर अपने एपीआई को ऑर्डर किए गए फ़ील्ड से कॉन्फ़िगर करना चाहते हैं, तो कृपया मैटियस नॉर्डबर्ग उत्तर को संयोजित करें:
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 वापस करने के लिए कैसे मजबूर करें?
अपडेट करें
मैंने बस नीचे की ओर देखा। ऐसा करने के लिए नीचे 'स्टीव' से जवाब देखें।
मूल
मैंने JsonConvert.SerializeObject(key)प्रतिबिंब के माध्यम से विधि कॉल का पालन किया (जहां कुंजी एक IList थी) और पाया कि JsonSerializerInternalWriter.SerializeList कहा जाता है। यह एक सूची लेता है और के माध्यम से छोरों
for (int i = 0; i < values.Count; i++) { ...
मूल्यों में IList पैरामीटर कहाँ लाया जाता है।
संक्षिप्त उत्तर है ... नहीं, फ़ील्ड को JSON स्ट्रिंग में सूचीबद्ध करने के क्रम को सेट करने के लिए कोई तरीका नहीं बनाया गया है।
JSON प्रारूप में फ़ील्ड का कोई क्रम नहीं है, इसलिए किसी आदेश को परिभाषित करने का कोई मतलब नहीं है।
{ id: 1, name: 'John' }के बराबर है { name: 'John', id: 1 }(दोनों एक कड़ाई से समकक्ष वस्तु उदाहरण का प्रतिनिधित्व करते हैं)