JsonConvert.SerializeObject
changes the sorting order of fields in JSON if you call the .GetProperty
method on the object being serialized in the child thread.
class Program
{
static void Main(string[] args)
{
var tasks = new List<Task>();
for (int i = 0; i < 10; i )
{
var task = Task.Factory.StartNew(() =>
{
var token = CreateRandomToken();
_ = typeof(TestObject).GetProperty("Version");
var str = JsonConvert.SerializeObject(token);
Console.WriteLine(str);
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
Console.ReadLine();
}
private static TestObject CreateRandomToken()
=> new TestObject { TokenHash = "123456789", Name = "Name", Version = "123" };
}
public class TestObject
{
public string TokenHash { get; set; }
public string Name { get; set; }
public string Version { get; set; }
}
As a result of executing this code, the following will be displayed on the console:
The Version
field is at the beginning of the JSON, not at the end
If we remove
_ = typeof(TestObject).GetProperty("Version");
- then the sorting of the fields will not change or if you call code in the main thread, then the sorting will not change either
if I decorate my object with the attributes [JsonProperty (Order = 1)]
then the sorting will not be the same as I indicated in the attributes
How can I fix it? fix without using the attr [JsonProperty (Order = 1)]
Updated: We use a JSON string to generate a digital signature if the order of the fields changes the digital signature will not be valid, so the order of the fields is important for me
CodePudding user response:
Because the default JsonSerializer
get properties using System.Type.GetProperties()
.
The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies. (Source Type.GetProperties Method)
In my opinion, you shouldn't care about the order of properties in Json. If the json consumer really need this contract, I think you should review your design.
An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array. (Source RFC 7159)
CodePudding user response:
It turns out that JsonConvert.SerializeObject
doesn't guarantee the default order of fields. To specify an explicit sorting, you can use the DefaultContractResolver
Thanks Andy for the idea!
Implementation of custom DefaultContractResolver
:
public class OrderedContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, memberSerialization).OrderBy(p=>p.PropertyName).ToList();
}
}
Usage example:
var jsonSerializerSettings = new JsonSerializerSettings {ContractResolver = new OrderedContractResolver()};
var str = JsonConvert.SerializeObject(token, jsonSerializerSettings);