68

I have two format of JSON which I want to Deserialize to one class. I know we can't apply two [JsonProperty] attribute to one property.

Can you please suggest me a way to achieve this?

string json1 = @"
    {
        'field1': '123456789012345',
        'specifications': {
            'name1': 'HFE'
        }
    }";

string json2 = @"
    {
        'field1': '123456789012345',
        'specifications': {
            'name2': 'HFE'
        }
    }";

public class Specifications
{
    [JsonProperty("name1")]
    public string CodeModel { get; set; }
}

public class ClassToDeserialize
{
    [JsonProperty("field1")]
    public string Vin { get; set; }

    [JsonProperty("specification")]
    public Specifications Specifications { get; set; }        
}

I want name1 and name2 both to be deserialize to name1 property of specification class.

PoLáKoSz
  • 355
  • 1
  • 6
  • 7
Vivek Tiwari
  • 731
  • 1
  • 6
  • 7
  • this seems like a design problem. But if you want to do it anyway, you could write a custom json converter and map the 2 names to name1 there. Here is an example of such a converter: http://stackoverflow.com/questions/36233759/web-api-2-custom-data-type-json-serialization/36243575#36243575 – Khanh TO May 01 '17 at 04:00
  • follow the steps here... http://stackoverflow.com/a/19885911/2445471 – khaled4vokalz May 01 '17 at 04:03
  • 1
    @Khanh TO Yes I know this is a bit strange requirement. actually we are getting data from two diff sources and both have diff format of data. what we are trying to do is to map it to a common format. coming to json converter part I didn't see any example where nested class fields could be mapped to two different names. it would be great if you could help. thanks in advance. – Vivek Tiwari May 01 '17 at 04:05
  • @khaled4vokalz I have already seen all of example we have on stack overflow. nothing suggest to have two names for one property of nested class :( – Vivek Tiwari May 01 '17 at 04:08

3 Answers3

188

A simple solution which does not require a converter: just add a second, private property to your class, mark it with [JsonProperty("name2")], and have it set the first property:

public class Specifications
{
    [JsonProperty("name1")]
    public string CodeModel { get; set; }

    [JsonProperty("name2")]
    private string CodeModel2 { set { CodeModel = value; } }
}

Fiddle: https://dotnetfiddle.net/z3KJj5

Brian Rogers
  • 118,414
  • 30
  • 277
  • 278
  • 2
    Very helpful, thanks. Small nuance I encountered is that I had to explicitly provide the JsonProperty attribute for private properties. Usually "name2" would automatically map to "Name2" without the JsonProperty attribute but it did not for me in this case. May have just been a quirk of our settings, but just in case it helps someone else. – Steve Cadwallader May 23 '18 at 13:06
  • 8
    @SteveCadwallader It's not a quirk. By design, Json.Net does not automatically serialize or deserialize private properties. Using the attribute signals that you *do* want it serialized/deserialized. – Brian Rogers May 23 '18 at 14:50
  • 2
    I also needed to set the Required property on the attribute -- by default, JsonProperty attribute's Required is set to "Required" rather than "Default"... haha. – Slate Jun 18 '20 at 18:35
  • 2
    Epic. If only ideas like this could earn as much money as the paperclip. – Frank Apr 18 '22 at 12:46
2

Tricking custom JsonConverter worked for me. Thanks @khaled4vokalz, @Khanh TO

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object instance = objectType.GetConstructor(Type.EmptyTypes).Invoke(null);
        PropertyInfo[] props = objectType.GetProperties();

        JObject jo = JObject.Load(reader);
        foreach (JProperty jp in jo.Properties())
        {
            if (string.Equals(jp.Name, "name1", StringComparison.OrdinalIgnoreCase) || string.Equals(jp.Name, "name2", StringComparison.OrdinalIgnoreCase))
            {
                PropertyInfo prop = props.FirstOrDefault(pi =>
                pi.CanWrite && string.Equals(pi.Name, "CodeModel", StringComparison.OrdinalIgnoreCase));

                if (prop != null)
                    prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
            }
        }

        return instance;
    }
Vivek Tiwari
  • 731
  • 1
  • 6
  • 7
1

I had the same use case, though in Java.

Resource that helped https://www.baeldung.com/json-multiple-fields-single-java-field

We can use a

@JsonProperty("main_label_to_serialize_and_deserialize")
@JsonAlias("Alternate_label_if_found_in_json_will_be_deserialized")

In your use case you could do

@JsonProperty("name1")
@JsonAlias("name2")
Peter Csala
  • 10,331
  • 15
  • 20
  • 47