Search code examples
c#jsonjson.net

Assign the same JsonProperty to two properties on class


When deserializing a JSON property from a request I want to use it for two different properties on my object. e.g;

public class Example
{
    [JsonProperty(PropertyName = "favoriteColor")]
    public string favoriteColor { get; set; }

    [JsonProperty(PropertyName = "favoriteColor")]
    public string oldFavoriteColor { get; set; }
}

However this causes an error:

A member with the name 'favoriteColor' already exists on 'Example'. Use the JsonPropertyAttribute to specify another name.

How do I do this when that's exactly what I intend?


Solution

  • There may be a better answer that involves taking a step back from the problem and seeing whether there's a totally different approach. In particular, why do you need a class that has two properties with the same value? Other than deserializing from JSON, will every other consumer of this class know that these two properties need to be kept in sync, and why?

    But this answers the immediate question.

    The [JsonProperty] attribute is used for both serializing and deserializing. If you had two properties with the same [JsonProperty] attribute then when you serialized the object to JSON you would have two properties with the same name.

    You can create a custom JSON serializer like this. As you can see, after an Example is deserialized it will populate the favoriteColor property with the value of the oldFavoriteColor property.

    public class ExampleConverter : CustomCreationConverter<Example>
    {
        public override Example Create(Type objectType)
        {
            return new Example();
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, 
            object existingValue, JsonSerializer serializer)
        {
            var result = (Example)base.ReadJson(reader, objectType, existingValue, 
                serializer);
            result.favoriteColor = result.oldFavoriteColor;
            return result;
        }
    }
    

    In order for this to work you also have to tell the serializer not to attempt to deserialize the favoriteColor property. Even without the [JsonProperty] attribute there's still a conflict between [JsonProperty(PropertyName = "favoriteColor")] and another property actually named "favoriteColor."

    public class Example
    {
        [JsonIgnore]
        public string favoriteColor { get; set; }
    
        [JsonProperty(PropertyName = "favoriteColor")]
        public string oldFavoriteColor { get; set; }
    }
    

    A unit test to confirm:

    public void DeserializerPopulatesFavoriteColorFromOldFavoriteColor()
    {
        var json = @"{ favoriteColor: ""Green""}";
        var deserialized = JsonConvert.DeserializeObject<Example>(json, new ExampleConverter());
        Assert.AreEqual("Green", deserialized.oldFavoriteColor);
        Assert.AreEqual(deserialized.oldFavoriteColor, deserialized.favoriteColor);
    }