Default camel case of property names in JSON serialization - c#

I have a bunch of classes that will be serialized to JSON at some point and for the sake of following both C# conventions on the back-end and JavaScript conventions on the front-end, I've been defining properties like this:
[JsonProperty(PropertyName="myFoo")]
public int MyFoo { get; set; }
So that in C# I can:
MyFoo = 10;
And in Javascript I can:
if (myFoo === 10)
But doing this for every property is tedious. Is there a quick and easy way to set the default way JSON.Net handles property names so it will automatically camel case unless told otherwise?

You can use the provided class Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver:
var serializer = new JsonSerializer
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var jobj = JObject.FromObject(request, serializer);
In other words, you don't have to create a custom resolver yourself.

When serializing your object, pass in some custom settings.
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var json = JsonConvert.SerializeObject(yourObject, settings);

Better to use the new CamelCaseNamingStrategy (since 9.0.1):
new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
It does not override custom names set by JsonPropert('Name') by default. (You can change the behaviour by CamelCaseNamingStrategy(bool, bool) ctor.) So, does not need to create custom class like #Matt Burland's answer.

JObject.FromObject uses default settings from JsonConvert defaults.
There is a func property that you can assign like this:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
and whenever you call Jobject.FromObject, it will use this func to construct settings.

Since the accepted answer is link-only, I'm adding the actual code I ended up using (in case the link dies). It's largely the same as what was in the link:
// Automatic camel casing because I'm bored of putting [JsonProperty] on everything
// See: http://harald-muehlhoff.de/post/2013/05/10/Automatic-camelCase-naming-with-JsonNET-and-Microsoft-Web-API.aspx#.Uv43fvldWCl
public class CamelCase : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization memberSerialization)
{
var res = base.CreateProperty(member, memberSerialization);
var attrs = member.GetCustomAttributes(typeof(JsonPropertyAttribute), true);
if (attrs.Any())
{
var attr = (attrs[0] as JsonPropertyAttribute);
if (res.PropertyName != null && attr.PropertyName != null)
res.PropertyName = attr.PropertyName;
}
return res;
}
}
The only change I made was the addition of attr.PropertyName != null to the if clause because of the case where I had added something like:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string SomeProperty { get; set; }
And didn't want to specify the PropertyName (so it's null). The above will be serialized in JSON as someProperty.

You can use a custom contract resolver:
class MyContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
foreach (var property in properties)
{
property.PropertyName = char.ToLower(property.PropertyName[0]) + string.Join("", property.PropertyName.Skip(1));
}
return properties;
}
}
And use it like:
class MyClass
{
public int MyProperty { get; set; }
public int MyProperty2 { get; set; }
}
var json = JsonConvert.SerializeObject(new MyClass(),
Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new MyContractResolver() });

In .NET 5.0 you can use System.Text.Json and specifiy the ProperyNamingPolicy inside the JsonSerializerOptions
System.Text.Json.JsonSerializerOptions.PropertyNamingPolicy
Here's a link to the Microsoft docs page on setting the property to use camel case.
var serializeOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
Class
public class WeatherForecastWithPropertyNameAttribute
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}
JSON output
{
"date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"summary": "Hot",
"Wind": 35
}

public static JsonSerializer FormattingData()
{
var jsonSerializersettings = new JsonSerializer {
ContractResolver = new CamelCasePropertyNamesContractResolver() };
return jsonSerializersettings;
}
public static JObject CamelCaseData(JObject jObject)
{
var expandoConverter = new ExpandoObjectConverter();
dynamic camelCaseData =
JsonConvert.DeserializeObject(jObject.ToString(),
expandoConverter);
return JObject.FromObject(camelCaseData, FormattingData());
}

Related

Deserializing json - mapping single property with no direct json match

I am trying to deserialize an existing JSON structure to into an object composed of a set of models. The naming in these models are not consistent and I was specifically asked to not change them (renaming, adding attributes, etc).
So, given this Json text (just a small sample):
{
"parameter": {
"alarms": [
{
"id": 1,
"name": "alarm1",
"type": 5,
"min": 0,
"max": 2
}],
"setting-active": true,
"setting-oneRun": true
}
}
would need to be mapped into these models:
public class Alarm
{
public int AlarmId { get; set; }
public string AlarmName { get; set; }
public AlarmType RbcType { get; set; }
public int MinimumTolerated { get; set; }
public int MaximumTolerated { get; set; }
}
public class Setting
{
public bool Active { get; set; }
public bool OneRun { get; set; }
}
public class Parameter
{
public List<Alarm> Alarms { get; set; }
public Setting ParameterSetting { get; set; }
}
So far, im writing a class that extends DefaultContractResolver and overrides maps property names.
MyCustomResolver so far:
public class MyCustomResolver : DefaultContractResolver
{
private Dictionary<string, string>? _propertyMappings;
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
//ModelMappings is a static class that will return a dictionary with mappings per ObjType being deserialized
_propertyMappings = ModelMappings.GetMapping(type);
return base.CreateProperties(type, memberSerialization);
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyMappings != null)
{
_propertyMappings.TryGetValue(propertyName, out string? resolvedName);
return resolvedName ?? base.ResolvePropertyName(propertyName);
}
return base.ResolvePropertyName(propertyName);
}
}
Code that Im using to deserialize:
var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new MyCustomResolver();
Parameter p = JsonConvert.DeserializeObject<Parameter>(jsonString, settings);
So I reached a point I need to somehow map the properties in Parameter to values located in the prev json node ("setting-active", "setting-oneRun"). I need to tell the deserializer where these values are.
Can this be done using an extension of DefaultContractResolver ?
I appreciate any tips pointing in the right direction
You can apply ModelMappings.GetMapping(objectType) in DefaultContractResolver.CreateObjectContract():
public class MyCustomResolver : DefaultContractResolver
{
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
var overrides = ModelMappings.GetMapping(objectType);
if (overrides != null)
{
foreach (var property in contract.Properties.Concat(contract.CreatorParameters))
{
if (property.UnderlyingName != null && overrides.TryGetValue(property.UnderlyingName, out var name))
property.PropertyName = name;
}
}
return contract;
}
}
Notes:
By applying the mappings in CreateObjectContract() you can remap both property names and creator parameter names.
Since the contract resolver is designed to resolve contracts for all types, storing a single private Dictionary<string, string>? _propertyMappings; doesn't really make sense.
Unlike your previous question, your current question shows properties from a nested c# object ParameterSetting getting percolated up to the parent object Parameter. Since a custom contract resolver is designed to generate the contract for a single type, it isn't suited to restructuring data between types. Instead, consider using a DTO or converter + DTO in such situations:
public class ParameterConverter : JsonConverter<Parameter>
{
record ParameterDTO(List<Alarm> alarms, [property: JsonProperty("setting-active")] bool? Active, [property: JsonProperty("setting-oneRun")] bool? OneRun);
public override void WriteJson(JsonWriter writer, Parameter? value, JsonSerializer serializer)
{
var dto = new ParameterDTO(value!.Alarms, value.ParameterSetting?.Active, value.ParameterSetting?.OneRun);
serializer.Serialize(writer, dto);
}
public override Parameter? ReadJson(JsonReader reader, Type objectType, Parameter? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var dto = serializer.Deserialize<ParameterDTO>(reader);
if (dto == null)
return null;
existingValue ??= new ();
existingValue.Alarms = dto.alarms;
if (dto.Active != null || dto.OneRun != null)
existingValue.ParameterSetting = new () { Active = dto.Active.GetValueOrDefault(), OneRun = dto.OneRun.GetValueOrDefault() };
return existingValue;
}
}
If your "real" model is too complex to define a DTO, you could create a JsonConverter<Paramater> that (de)serializes the JSON into an intermediate JToken hierarchy, then restructures that. See e.g. this answer to Can I serialize nested properties to my class in one operation with Json.net?.
In some cases, the custom naming of your properties is just camel casing. To camel case property names without the need for explicit overrides, set MyCustomResolver.NamingStrategy to CamelCaseNamingStrategy e.g. as follows:
var settings = new JsonSerializerSettings
{
DateFormatString = "YYYY-MM-DD",
// Use CamelCaseNamingStrategy since many properties in the JSON are just camel-cased.
ContractResolver = new MyCustomResolver { NamingStrategy = new CamelCaseNamingStrategy() },
Converters = { new ParameterConverter() },
};
Demo fiddle here.
I think that the best way to "KEEP IT SIMPLE", you need to define an object that has exactly the properties of the json. Then you can use a library like "Automapper" to define rules of mapping between the "json object" and the "business object".

System.Text.Json - Use custom JsonConverter conditionally depending on field attribute

I have a custom attribute [Foo]
implemented as follows:
public class FooAttribute
: Attribute
{
}
Now I want to use the System.Text.Json.JsonSerializer to step into each field that has that attribute, in order to manipulate how is serialized and deserialized.
For example, if I have the following class
class SampleInt
{
[Foo]
public int Number { get; init; }
public int StandardNumber { get; init; }
public string Text { get; init; }
}
when I serialize an instance of this class, I want a custom int JsonConverter to apply only for that field.
public class IntJsonConverter
: JsonConverter<int>
{
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// do whatever before reading if the text starts with "potato". But this should be triggered only if destination type has the Foo attribute. How?
return reader.GetInt32();
}
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
writer.WriteStringValue("potato" + value.ToString());
}
}
so that the serialization for
var sample =
new SampleInt
{
Number = 123,
StandardNumber = 456
Text = "bar"
};
like this
var serializeOptions = new JsonSerializerOptions();
var serializeOptions.Converters.Add(new IntJsonConverter());
var resultJson = JsonSerializer.Serialize(sample, serializeOptions);
results on the following json
{
"number": "potato123",
"standardNumber": 456,
"text": "bar"
}
and not in
{
"number": "potato123",
"standardNumber": "potato456",
"text": "bar"
}
In a similar manner, I want the deserialization to be conditional, and only use the custom converter if the destination field has the [Foo] attribute.
With Newtonsoft, this is possible using Contract Resolvers and overriding CreateProperties method like this.
public class SerializationContractResolver
: DefaultContractResolver
{
private readonly ICryptoTransform _encryptor;
private readonly FieldEncryptionDecryption _fieldEncryptionDecryption;
public SerializationContractResolver(
ICryptoTransform encryptor,
FieldEncryptionDecryption fieldEncryptionDecryption)
{
_encryptor = encryptor;
_fieldEncryptionDecryption = fieldEncryptionDecryption;
NamingStrategy = new CamelCaseNamingStrategy();
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
foreach (var jsonProperty in properties)
{
var hasAttribute = HasAttribute(type, jsonProperty);
if (hasAttribute)
{
var serializationJsonConverter = new MyJsonConverter();
jsonProperty.Converter = serializationJsonConverter;
}
}
return properties;
}
private bool HasAttribute(Type type, JsonProperty jsonProperty)
{
var propertyInfo = type.GetProperty(jsonProperty.UnderlyingName);
if (propertyInfo is null)
{
return false;
}
var hasAttribute =
propertyInfo.CustomAttributes
.Any(x => x.AttributeType == typeof(FooAttribute));
var propertyType = propertyInfo.PropertyType;
var isSimpleValue = propertyType.IsValueType || propertyType == typeof(string);
var isSupportedField = isSimpleValue && hasPersonalDataAttribute;
return isSupportedField;
}
}
But I don't want to use Newtonsoft. I want to use the new dotnet System.Text.Json serializer. Is it possible to use it in a similar granular way?

Using property attributes to define which JsonConverter to use

I have a need to specify a JsonConverter for properties which are decorated with a specific attribute, in this case [DataType(DataType.PostalCode)].
I already have a custom JsonConverter for which I have set the CanConvert method as follows:
public override bool CanConvert(Type objectType) => objectType == typeof(string);
How can I make sure the PostcodeJsonConverter is used instead when the API encounters a PostalCode property?
[DataType(DataType.PostalCode)]
public string Postcode { get; set; }
I've tried the following but I suspect the DataType attribute is not available at this point.
public override bool CanConvert(Type objectType) =>
objectType == typeof(string) &&
objectType.GetCustomAttributes(true)
.OfType<DataTypeAttribute>()
.Any(dta => dta.DataType == DataType.PostalCode);
Do I need to decorate my model as follows instead?
[DataType(DataType.PostalCode)]
[JsonConverter(typeof(PostcodeJsonConverter))]
public string Postcode { get; set; }
You can make a custom ContractResolver that looks for your DataType attribute on each property and maps the values to the appropriate converter. Here is the code you would need:
public class DataTypeResolver : DefaultContractResolver
{
private Dictionary<DataType, JsonConverter> ConvertersByDataType { get; set; }
public DataTypeResolver()
{
// Adjust this list to match your actual data types and converters
ConvertersByDataType = new Dictionary<DataType, JsonConverter>
{
{ DataType.PostalCode, new PostalCodeConverter() },
{ DataType.PhoneNumber, new PhoneNumberConverter() },
};
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
var att = prop.AttributeProvider.GetAttributes(true).OfType<DataTypeAttribute>().FirstOrDefault();
if (att != null)
{
JsonConverter converter;
if (ConvertersByDataType.TryGetValue(att.DataType, out converter))
{
prop.Converter = converter;
}
}
return prop;
}
}
Then pass the resolver to SerializeObject and/or DeserializeObject via the settings:
var settings = new JsonSerializerSettings
{
ContractResolver = new DataTypeResolver()
};
string json = JsonConvert.SerializeObject(yourObject, settings);
Here is a working demo: https://dotnetfiddle.net/k1kWv5
You can add Converters to the JsonSerializerSettings. So instead of decorating everything, you may as well add your PostcodeJsonConverter there (depending on how often it is used, a decorator may be better though):
For aspnet core defaults:
services.AddMvc().AddJsonOptions(o => o.SerializerSettings.Converters.Add(new PostcodeJsonConverter()))
For JsonConvert:
JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
Converters = { new PostcodeJsonConverter() }
});

NewtonSoft add JSONIGNORE at runTime

Am looking to Serialize a list using NewtonSoft JSON and i need to ignore one of the property while Serializing and i got the below code
public class Car
{
// included in JSON
public string Model { get; set; }
// ignored
[JsonIgnore]
public DateTime LastModified { get; set; }
}
But am using this Specific class Car in many places in my application and i want to Exclude the option only in one place.
Can i dynamically add [JsonIgnore] in the Specific Place where i need ? How do i do that ?
No need to do the complicated stuff explained in the other answer.
NewtonSoft JSON has a built-in feature for that:
public bool ShouldSerializeINSERT_YOUR_PROPERTY_NAME_HERE()
{
if(someCondition){
return true;
}else{
return false;
}
}
It is called "conditional property serialization" and the documentation can be found here.
Warning: first of all, it is important to get rid of [JsonIgnore] above your {get;set;} property. Otherwise it will overwrite the ShouldSerializeXYZ behavior.
I think it would be best to use a custom IContractResolver to achieve this:
public class DynamicContractResolver : DefaultContractResolver
{
private readonly string _propertyNameToExclude;
public DynamicContractResolver(string propertyNameToExclude)
{
_propertyNameToExclude = propertyNameToExclude;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
// only serializer properties that are not named after the specified property.
properties =
properties.Where(p => string.Compare(p.PropertyName, _propertyNameToExclude, true) != 0).ToList();
return properties;
}
}
The LINQ may not be correct, I haven't had a chance to test this. You can then use it as follows:
string json = JsonConvert.SerializeObject(car, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver("LastModified") });
Refer to the documentation for more information.
Based on #Underscore post above, I created a list of properties to exclude on serialization.
public class DynamicContractResolver : DefaultContractResolver {
private readonly string[] props;
public DynamicContractResolver(params string[] prop) {
this.props = prop;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
// return all the properties which are not in the ignore list
retval = retval.Where(p => !this.props.Contains(p.PropertyName)).ToList();
return retval;
}
}
Use:
string json = JsonConvert.SerializeObject(car, Formatting.Indented,
new JsonSerializerSettings { ContractResolver = new DynamicContractResolver("ID", "CreatedAt", "LastModified") });
With the reference Dynamically rename or ignore properties without changing the serialized class we can achieve JsonIgnore at run time. It's a workable solution.
Consider Person Class for example:
public class Person
{
// ignore property
[JsonIgnore]
public string Title { get; set; }
// rename property
[JsonProperty("firstName")]
public string FirstName { get; set; }
}
Step 1: Create Class "PropertyRenameAndIgnoreSerializerContractResolver"
public class PropertyRenameAndIgnoreSerializerContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _ignores;
private readonly Dictionary<Type, Dictionary<string, string>> _renames;
public PropertyRenameAndIgnoreSerializerContractResolver()
{
_ignores = new Dictionary<Type, HashSet<string>>();
_renames = new Dictionary<Type, Dictionary<string, string>>();
}
public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
{
if (!_ignores.ContainsKey(type))
_ignores[type] = new HashSet<string>();
foreach (var prop in jsonPropertyNames)
_ignores[type].Add(prop);
}
public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
{
if (!_renames.ContainsKey(type))
_renames[type] = new Dictionary<string, string>();
_renames[type][propertyName] = newJsonPropertyName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.DeclaringType, property.PropertyName))
{
property.ShouldSerialize = i => false;
property.Ignored = true;
}
if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
property.PropertyName = newJsonPropertyName;
return property;
}
private bool IsIgnored(Type type, string jsonPropertyName)
{
if (!_ignores.ContainsKey(type))
return false;
return _ignores[type].Contains(jsonPropertyName);
}
private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
{
Dictionary<string, string> renames;
if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
{
newJsonPropertyName = null;
return false;
}
return true;
}
}
Step 2: Add code in your method where Jsonignore want to apply
var person = new Person();
var jsonResolver = new PropertyRenameAndIgnoreSerializerContractResolver();
jsonResolver.IgnoreProperty(typeof(Person), "Title");
jsonResolver.RenameProperty(typeof(Person), "FirstName", "firstName");
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = jsonResolver;
var json = JsonConvert.SerializeObject(person, serializerSettings);
With respect to all correct answers I would like to add something. When you have nested properties with the same name so ignoring will effect on all properties with the same name. If you like to ignore a specific property you can do something like this:
public class DynamicContractResolver : DefaultContractResolver
{
private readonly string[] props;
public DynamicContractResolver(params string[] prop)
{
this.props = prop;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
return retval.Where(p => !this.props.Contains(p.DeclaringType.FullName + "." + p.PropertyName)).ToList();
}
}
then when you want to use it you can say:
var values = await _dbContext
.Set<EntityName>()
.Where(...).ToList();
var json = JsonConvert.SerializeObject(values, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new DynamicContractResolver("Entities.Contact.Address1","Entities.User.Name","Entities.Event.Name")
});
The Address1 will be ignored in Contact not anywhere else.
Try this:
public static void IgnoreProperty<T, TR>(this T parameter, Expression<Func<T, TR>> propertyLambda)
{
var parameterType = parameter.GetType();
var propertyName = propertyLambda.GetReturnedPropertyName();
if (propertyName == null)
{
return;
}
var jsonPropertyAttribute = parameterType.GetProperty(propertyName).GetCustomAttribute<JsonPropertyAttribute>();
jsonPropertyAttribute.DefaultValueHandling = DefaultValueHandling.Ignore;
}
public static string GetReturnedPropertyName<T, TR>(this Expression<Func<T, TR>> propertyLambda)
{
var member = propertyLambda.Body as MemberExpression;
var memberPropertyInfo = member?.Member as PropertyInfo;
return memberPropertyInfo?.Name;
}
So you can do this:
carObject.IgnoreProperty(so => so.LastModified);
Based on the accepted answer it would be like:
[JsonIgnore]
public bool JsonIgnore { get; set; }
public bool ImageModified { get; set; }
public bool ShouldSerializeImageModified() => !JsonIgnore;
Whenever JsonIgnore is set to true it means that ImageModified won't be serialized, and JsonIgnore is ignored because of [JsonIgnore].
If there is a need to write code this way, it might be an indication of poor design. Probably there needs to be a DTO or ViewModel in the system, unless you want to dynamically disable/enable serialization of some properties.

How can I sort the JSON output using JSON.NET? [duplicate]

Is there a way to specify the order of fields in a serialized JSON object using JSON.NET?
It would be sufficient to specify that a single field always appear first.
The supported way is to use the JsonProperty attribute on the class properties that you want to set the order for. Read the JsonPropertyAttribute order documentation for more information.
Pass the JsonProperty an Order value and the serializer will take care of the rest.
[JsonProperty(Order = 1)]
This is very similar to the
DataMember(Order = 1)
of the System.Runtime.Serialization days.
Here is an important note from #kevin-babcock
... setting the order to 1 will only work if you set an order greater than 1 on all other properties. By default any property without an Order setting will be given an order of -1. So you must either give all serialized properties and order, or set your first item to -2
You can actually control the order by implementing IContractResolver or overriding the DefaultContractResolver's CreateProperties method.
Here's an example of my simple implementation of IContractResolver which orders the properties alphabetically:
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();
}
}
And then set the settings and serialize the object, and the JSON fields will be in alphabetical order:
var settings = new JsonSerializerSettings()
{
ContractResolver = new OrderedContractResolver()
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
In my case Mattias' answer didn't work. The CreateProperties method was never called.
After some debugging of Newtonsoft.Json internals, I came up with another solution.
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;
}
}
In my case niaher's solution did not work because it didn't handle objects in arrays.
Based on his solution this is what I came up with
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;
}
}
}
This will work for normal classes, dictionaries and ExpandoObject (dynamic object) as well.
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);
If you just want to pull a single attribute up to the front without thinking about the perhaps unintuitive number system, just use int.MinValue.
[JsonProperty(Order = int.MinValue)]
As Charlie noted, you can somewhat control the ordering of the JSON properties by ordering the properties in the class itself. Unfortunately, this approach doesn't work for properties inherited from a base class. The base class properties will be ordered as they are laid out in code, but will appear before the base class properties.
And for anyone wondering why you might want to alphabetize JSON properties, it's a whole lot easier to work with raw JSON files, particularly for classes with lots of properties, if they are ordered.
If you don't want to put a JsonProperty Order attribute on every class property, then its very simple to make your own ContractResolver...
The IContractResolver interface provides a way to customize how the JsonSerializer serializes and deserializes .NET objects to JSON without placing attributes on your classes.
Like this:
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;
}
}
Implement:
var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
Actually, since my Object was already a JObject, I used the following solution:
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);
}
}
and then use it like this:
string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
The following recursive method uses reflection to sort the internal token list on an existing JObject instance rather than creating a brand new sorted object graph. This code relies on internal Json.NET implementation details and should not be used in production.
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);
}
}
If you control (i.e. write) the class, put the properties in alphabetical order and they will serialize in alphabetical order when JsonConvert.SerializeObject() is called.
I want to serialize an comblex object and keep the order of the properties as they where defined in code. I can't just add [JsonProperty(Order = 1)] because the class itself is out of my scope.
This solution also takes into account that properties which are defined in a base class should have a higher priority.
This may not be bulletproof, since nowhere is defined that the MetaDataAttribute ensures the correct order, but it seems to work. For my use case this is ok. since I only want to maintain human readability for an auto generated config file.
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();
}
}
If you want to globally configure your API with ordered fields, please combine Mattias Nordberg answer:
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();
}
}
with my answer here:
How to force ASP.NET Web API to always return JSON?
UPDATE
I just saw the downvotes. Please see the answer from 'Steve' below for how to do this.
ORIGINAL
I followed the JsonConvert.SerializeObject(key) method call via reflection (where key was an IList) and found that JsonSerializerInternalWriter.SerializeList gets called. It takes a list and loops through via
for (int i = 0; i < values.Count; i++) { ...
where values is the IList parameter brought in.
Short answer is...No, there's no built in way to set the order the fields are listed in the JSON string.
There's no order of fields in the JSON format so defining an order doesn't make sense.
{ id: 1, name: 'John' } is equivalent to { name: 'John', id: 1 } (both represent a strictly equivalent object instance)

Categories

Resources