代码之家  ›  专栏  ›  技术社区  ›  Kevin Montrose

使用json.net的序列化字段顺序

  •  108
  • Kevin Montrose  · 技术社区  · 16 年前

    有没有一种方法可以指定序列化JSON对象中字段的顺序 JSON.NET ?

    只需指定一个字段总是首先出现就足够了。

    13 回复  |  直到 7 年前
        1
  •  -3
  •   DougJones    16 年前

    我跟着 JsonConvert.SerializeObject(key) 通过反射调用方法(其中键是IList),发现调用了JSonSerializeInternalWriter.SerializeList。它需要一个列表并通过

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

    其中,values是引入的ilist参数。

    简短的回答是…不,没有内置的方法来设置字段在JSON字符串中列出的顺序。

        2
  •  207
  •   Steve    7 年前

    支持的方法是使用 JsonProperty 要为其设置顺序的类属性的属性。阅读 JsonPropertyAttribute order documentation 更多信息。

    通过 JSON特性 安 Order 值和序列化程序将处理其余的。

     [JsonProperty(Order = 1)]
    

    这和

     DataMember(Order = 1) 
    

    的 System.Runtime.Serialization 天。

    这是来自@kevin babcock的一个重要提示

    …只有在对所有其他属性设置大于1的顺序时,才能将顺序设置为1。默认情况下,没有订单设置的任何属性都将得到-1的订单。因此,必须给出所有序列化属性和顺序,或者将第一个项设置为-2

        3
  •  112
  •   Mattias Nordberg    14 年前

    您可以通过实现 IContractResolver 或覆盖 DefaultContractResolver 的 CreateProperties 方法。

    下面是我简单实现 IContractoresolver公司 按字母顺序排列属性:

    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);
    
        4
  •  15
  •   niaher    11 年前

    在我看来,马蒂亚斯的回答不起作用。这个 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;
        }
    }
    
        5
  •  8
  •   Tuan-Tu Tran    9 年前

    在我的例子中,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;
            }
        }
    }
    
        6
  •  3
  •   Jack Bond    9 年前

    正如Charlie所指出的,通过对类本身的属性进行排序,您可以在一定程度上控制JSON属性的排序。不幸的是,这种方法不适用于继承自基类的属性。将按照代码中的布局对基类属性进行排序,但这些属性将显示在基类属性之前。

    对于任何想知道为什么要按字母顺序排列JSON属性的人来说,使用原始JSON文件要容易得多,尤其是对于具有许多属性的类(如果它们是有序的)。

        7
  •  0
  •   Nathan Baulch    14 年前

    以下递归方法使用反射对现有的 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);
        }
    }
    
        8
  •  0
  •   Danny R    10 年前

    实际上,由于我的对象已经是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));
    
        9
  •  0
  •   Tunaki    9 年前

    如果控制(即写入)类,请按字母顺序放置属性,当 JsonConvert.SerializeObject() 被称为。

        10
  •  0
  •   Jürgen Steinblock    7 年前

    我想序列化一个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();
        }
    }
    
    
    
        11
  •  0
  •   Jay Shah    7 年前

    这也适用于普通类、字典和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);
    
        12
  •  -1
  •   Carlo Saccone    8 年前

    如果要全局配置带有序字段的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();
        }
    }
    

    我的答案是:

    How to force ASP.NET Web API to always return JSON?

        13
  •  -6
  •   Darin Dimitrov    16 年前

    JSON格式中没有字段的顺序,因此定义顺序没有意义。

    { id: 1, name: 'John' } 等于 { name: 'John', id: 1 } (两者都表示严格等效的对象实例)