Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use different name for serializing and deserializing with Json.Net

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.

like image 862
Aetherix Avatar asked Jun 19 '17 13:06

Aetherix


3 Answers

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!'}"

like image 155
NtFreX Avatar answered Nov 09 '22 16:11

NtFreX


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.

like image 2
lee_mcmullen Avatar answered Nov 09 '22 16:11

lee_mcmullen


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;
}
like image 1
Red Avatar answered Nov 09 '22 18:11

Red