JSON.NETを使用してシリアル化されたJSONオブジェクトのフィールドの順序を指定する方法はありますか?
単一のフィールドが常に最初に表示されるように指定するだけで十分です。
JSON.NETを使用してシリアル化されたJSONオブジェクトのフィールドの順序を指定する方法はありますか?
単一のフィールドが常に最初に表示されるように指定するだけで十分です。
回答:
サポートされている方法は、 JsonProperty
、順序を設定するクラスプロパティの属性ことです。詳細については、JsonPropertyAttribute注文ドキュメントをご覧ください。
渡しJsonProperty
ANのOrder
値をとシリアライザは、残りの世話をします。
[JsonProperty(Order = 1)]
これは
DataMember(Order = 1)
の System.Runtime.Serialization
日。
ここに@ kevin-babcockからの重要なメモがあります
...順序を1に設定すると、他のすべてのプロパティで1より大きい順序を設定した場合にのみ機能します。デフォルトでは、Order設定のないプロパティには、-1の順序が与えられます。したがって、シリアル化されたすべてのプロパティと順序を指定するか、最初の項目を-2に設定する必要があります
Order
プロパティを使用すると、JsonPropertyAttribute
フィールドがシリアル化/非シリアル化される順序を制御できます。ただし、順序を1に設定すると、他のすべてのプロパティで1より大きい順序を設定した場合にのみ機能します。デフォルトでは、Order設定のないプロパティには、-1の順序が与えられます。したがって、シリアル化されたすべてのプロパティと順序を指定するか、最初の項目を-2に設定する必要があります。
JavaScriptSerializer
ますか?
のメソッドを実装IContractResolver
またはオーバーライドすることで、実際に順序を制御できます。DefaultContractResolver
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;
}
}
}
これは、通常のクラス、辞書、およびExpandoObject(動的オブジェクト)でも機能します。
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
注文のために、または他のカスタマイズにフックしません。オブジェクトの列挙子からエントリをそのまま取得します。JSON.netを作成するSortedDictionary
かSortedList
、強制的に実行する必要があるようです。提案された機能の提案:github.com/JamesNK/Newtonsoft.Json/issues/2270
JsonProperty
Order
すべてのクラスプロパティに属性を設定したくない場合は、独自のContractResolverを作成するのが非常に簡単です...
IContractResolverインターフェイスは、JsonSerializerがクラスに属性を配置せずに.NETオブジェクトをJSONにシリアル化および逆シリアル化する方法をカスタマイズする方法を提供します。
このような:
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);
}
}
実際、私のオブジェクトはすでにJObjectだったので、次のソリューションを使用しました。
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();
}
}
順序付けられたフィールドでAPIをグローバルに構成する場合は、Mattias Nordbergの回答を組み合わせてください:
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();
}
}
ここで私の答え:
更新
反対票を見たところです。これを行う方法については、以下の「スティーブ」からの回答を参照してください。
元の
JsonConvert.SerializeObject(key)
リフレクションを介してメソッドの呼び出しを追跡し(キーはIListでした)、JsonSerializerInternalWriter.SerializeListが呼び出されることがわかりました。リストを受け取り、viaを介してループします
for (int i = 0; i < values.Count; i++) { ...
ここで、valuesは、取り込まれたIListパラメータです。
短い答えは...いいえ、JSON文字列にフィールドがリストされる順序を設定する組み込みの方法はありません。
JSON形式にはフィールドの順序がないため、順序を定義しても意味がありません。
{ id: 1, name: 'John' }
と等価です{ name: 'John', id: 1 }
(どちらも厳密に等価なオブジェクトインスタンスを表します)