I am receiving JSON data from a web API that looks like this:
[
{
"id": 1
"error_message": "An error has occurred!"
}
]
I deserialize this data to objects of the following type:
public class ErrorDetails
{
public int Id { get; set; }
[JsonProperty("error_message")]
public string ErrorMessage { get; set; }
}
Later in my application I would like to serialize the ErrorDetails object again to JSON but using the property name ErrorMessage
instead of error_message
. So the result would look like this:
[
{
"Id": 1
"ErrorMessage": "An error has occurred!"
}
]
Is there an easy way I can accomplish this with Json.Net? Perhaps using a custom resolver and some attributes like:
public class ErrorDetails
{
public int Id { get; set; }
[SerializeAs("ErrorMessage")]
[DeserializeAs("error_message")]
public string ErrorMessage { get; set; }
}
But the resolver doesn't tell me when I'm serializing or deserializing.
You can make use of the JsonSerializerSettings
, the ContractResolver and the NamingStrategy.
public class ErrorDetails
{
public int Id { get; set; }
public string ErrorMessage { get; set; }
}
var json = "{'Id': 1,'error_message': 'An error has occurred!'}";
For dezerialization you could use the SnakeCaseNamingStrategy
.
var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};
var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);
To serialize the object again you dont have to change the JsonSerializerSettings
as the default will use the property name.
var jsonNew = JsonConvert.SerializeObject(obj);
jsonNew = "{'Id': 1,'ErrorMessage': 'An error has occurred!'}"
Or you could create a contract resolver which can decide which name to use. Then you can decide when you dezerialize and serialize if you want to use the pascal case name format or the one with the underscore.
public class CustomContractResolver : DefaultContractResolver
{
public bool UseJsonPropertyName { get; }
public CustomContractResolver(bool useJsonPropertyName)
{
UseJsonPropertyName = useJsonPropertyName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (!UseJsonPropertyName)
property.PropertyName = property.UnderlyingName;
return property;
}
}
public class ErrorDetails
{
public int Id { get; set; }
[JsonProperty("error_message")]
public string ErrorMessage { get; set; }
}
var json = "{'Id': 1,'error_message': 'An error has occurred!'}";
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CustomContractResolver(false)
};
var dezerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver(true)
};
var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);
var jsonNew = JsonConvert.SerializeObject(obj, serializerSettings);
jsonNew = "{'Id': 1,'ErrorMessage': 'An error has occurred!'}"
Another way of achieving a different property name when serialising vs deserisalising is by using the ShouldSerialize
method: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm#ShouldSerialize
The docs say:
To conditionally serialize a property, add a method that returns boolean with the same name as the property and then prefix the method name with
ShouldSerialize
. The result of the method determines whether the property is serialized. If the method returns true then the property will be serialized, if it returns false then the property will be skipped.
E.g:
public class ErrorDetails
{
public int Id { get; set; }
// This will deserialise the `error_message` property from the incoming json into the `GetErrorMessage` property
[JsonProperty("error_message")]
public string GetErrorMessage { get; set; }
// If this method returns false then the property after the `ShouldSerialize` prefix will not be serialised into the output
public bool ShouldSerializeGetErrorMessage() => false;
// The serialised output will return `ErrorMessage` with the value from `GetErrorMessage` i.e. `error_message` in the original json
public string ErrorMessage { get { return GetErrorMessage; } }
}
This results in slightly more overhead so be careful if dealing with lots of properties or with lots of data but for small payloads, and if you don't mind messing up your DTO class a little, then this could be a quicker solution than writing custom contract resolvers etc.
I liked the answer by @lee_mcmullen, and implemented it in my own code. Now I think I've found a slightly neater version.
public class ErrorDetails
{
public int Id { get; set; }
// This will deserialise the `error_message` property from the incoming json and store it in the new `GetErrorMessage` property
[JsonProperty("error_message")]
public string GetErrorMessage { get { return ErrorMessage; } set { ErrorMessage = value; } }
// If this method returns false then the property after the `ShouldSerialize` prefix will not be serialised into the output
public bool ShouldSerializeGetErrorMessage() => false;
// The serialised output will return `ErrorMessage` with the value set from `GetErrorMessage` i.e. `error_message` in the original json
public string ErrorMessage { get; set; }
}
The reason I like this better is that in more complicated models it allows for inheritance while keeping all of the "old" custom stuff separate
public class ErrorDetails
{
public int Id { get; set; }
public string ErrorMessage { get; set; }
}
// This is our old ErrorDetails that hopefully we can delete one day
public class OldErrorDetails : ErrorDetails
{
// This will deserialise the `error_message` property from the incoming json and store it in the new `GetErrorMessage` property
[JsonProperty("error_message")]
public string GetErrorMessage { get { return ErrorMessage; } set { ErrorMessage = value; } }
// If this method returns false then the property after the `ShouldSerialize` prefix will not be serialised into the output
public bool ShouldSerializeGetErrorMessage() => false;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With