web-dev-qa-db-ja.com

JSON.NETを使用したシリアル化されたフィールドの順序

JSON.NET を使用して、シリアル化されたJSONオブジェクトのフィールドの順序を指定する方法はありますか?

単一のフィールドが常に最初に表示されるように指定すれば十分です。

116
Kevin Montrose

サポートされている方法は、順序を設定するクラスプロパティでJsonProperty属性を使用することです。詳細については、 JsonPropertyAttribute order documentation をお読みください。

JsonPropertyOrderの値を渡すと、シリアライザーが残りを処理します。

 [JsonProperty(Order = 1)]

これは非常に似ています

 DataMember(Order = 1) 

System.Runtime.Serialization 日々。

@ kevin-babcockからの重要なメモです

...順序を1に設定しても、他のすべてのプロパティで1より大きい順序を設定した場合にのみ機能します。デフォルトでは、順序設定のないプロパティには順序-1が与えられます。そのため、すべてのシリアル化されたプロパティと順序を指定するか、最初のアイテムを-2に設定する必要があります

221
Steve

IContractResolverを実装するか、DefaultContractResolverCreatePropertiesメソッドをオーバーライドすることで、実際に順序を制御できます。

プロパティをアルファベット順に並べる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);
118

私の場合、マティアスの答えはうまくいきませんでした。 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;
    }
}
15
niaher

私の場合、ニアハーのソリューションは配列内のオブジェクトを処理しなかったため機能しませんでした。

彼のソリューションに基づいて、これが私が思いついたものです

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;
        }
    }
}
9
Tuan-Tu Tran

チャーリーが述べたように、クラス自体のプロパティを順序付けることで、JSONプロパティの順序を多少制御できます。残念ながら、このアプローチは基本クラスから継承されたプロパティでは機能しません。基本クラスのプロパティは、コードでレイアウトされているとおりに順序付けられますが、基本クラスのプロパティの前に表示されます。

そして、なぜJSONプロパティをアルファベット順にしたいのか疑問に思う人にとっては、特にプロパティが多数あるクラスの場合、生のJSONファイルを操作するほうがずっと簡単です。

3
Jack Bond

これは、通常のクラス、辞書、および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);
2
Jay Shah

実際、私のオブジェクトはすでに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));
0
Danny R

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();
    }
}
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
Nathan Baulch

クラスを制御(つまり、書き込み)する場合、プロパティをアルファベット順に並べると、JsonConvert.SerializeObject()が呼び出されたときにアルファベット順にプロパティがシリアル化されます。

0
Charlie