Порядок использования сериализованных полей JSON.NET


есть ли способ указать порядок полей в сериализованном объекте JSON с помощью JSON.NET?

было бы достаточно указать, что одно поле всегда появляется первым.

11 91

11 ответов:

я следовал JsonConvert.SerializeObject(key) вызов метода через отражение (где ключ был IList) и обнаружил, что JsonSerializerInternalWriter.SerializeList получает вызов. Он принимает список и петли через via

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

где значения-это введенный параметр IList.

короткий ответ is...No, нет встроенного способа установить порядок, в котором поля перечислены в строке JSON.

поддерживаемый способ-использовать JsonProperty атрибут для свойств класса, для которых вы хотите установить порядок. прочитайте документацию по заказу JsonPropertyAttribute для получения дополнительной информации.

передать JsonProperty an Order значение и сериализатор позаботятся об остальном.

 [JsonProperty(Order = 1)]

это очень похоже на

 DataMember(Order = 1) 

на System.Runtime.Serialization дней.

вы можете фактически контролировать порядок, реализуя 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 в алфавитном порядке, гораздо проще работать с файлами raw JSON, особенно для классов с большим количеством свойств, если им прикажут.

следующий рекурсивный метод использует отражение для сортировки внутреннего списка маркеров на существующем 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));

Если вы управляете (т. е. пишете) классом, поместите свойства в алфавитном порядке, и они будут сериализованы в алфавитном порядке, когда JsonConvert.SerializeObject() называется.

Если вы хотите глобально настроить свой API с упорядоченными полями, пожалуйста, объедините ответ Маттиаса Нордберга:

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 веб-API всегда возвращает JSON?

в формате JSON нет порядка полей, поэтому определение порядка не имеет смысла.

{ id: 1, name: 'John' } эквивалентно { name: 'John', id: 1 } (оба представляют строго эквивалентный экземпляр объекта)