multiple JsonProperty Name assigned to single property

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.

52865 次浏览

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;
}

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

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")

You could even do this for more than 2 names.

@JsonProperty("name1")
@JsonAlias({"name2","name3","name4"})

You can do it using a JsonConverter.

It's useful for example when you consume some data from 3rd party services and they keep changing property names and then going back to previous property names. :D

The following code shows how to deserialize from multiple property names to the same class property decorated with a [JsonProperty(PropertyName = "EnrollmentStatusEffectiveDateStr")] attribute.

The class MediCalFFSPhysician is also decorated with the custom JsonConverter: [JsonConverter(typeof(MediCalFFSPhysicianConverter))]

Note that _propertyMappings dictionary holds the possible property names that should be mapped to the property EnrollmentStatusEffectiveDateStr:

private readonly Dictionary<string, string> _propertyMappings = new()
{
{"Enrollment_Status_Effective_Dat", "EnrollmentStatusEffectiveDateStr"},
{"Enrollment_Status_Effective_Date", "EnrollmentStatusEffectiveDateStr"},
{"USER_Enrollment_Status_Effectiv", "EnrollmentStatusEffectiveDateStr"}
};

Full code:

    // See https://aka.ms/new-console-template for more information
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Reflection;
using System.Text.Json;


internal class JSONDeserialization
{
private static void Main(string[] args)
{
var jsonPayload1 = $"\{\{\"Enrollment_Status_Effective_Dat\":\"2022/10/13 19:00:00+00\"}}";
var jsonPayload2 = $"\{\{\"Enrollment_Status_Effective_Date\":\"2022-10-13 20:00:00+00\"}}";
var jsonPayload3 = $"\{\{\"USER_Enrollment_Status_Effectiv\":\"2022-10-13 21:00:00+00\"}}";


var deserialized1 = JsonConvert.DeserializeObject<MediCalFFSPhysician>(jsonPayload1);
var deserialized2 = JsonConvert.DeserializeObject<MediCalFFSPhysician>(jsonPayload2);
var deserialized3 = JsonConvert.DeserializeObject<MediCalFFSPhysician>(jsonPayload3);


Console.WriteLine(deserialized1.Dump());
Console.WriteLine(deserialized2.Dump());
Console.WriteLine(deserialized3.Dump());


Console.ReadKey();
}
}


public class MediCalFFSPhysicianConverter : JsonConverter
{
private readonly Dictionary<string, string> _propertyMappings = new()
{
{"Enrollment_Status_Effective_Dat", "EnrollmentStatusEffectiveDateStr"},
{"Enrollment_Status_Effective_Date", "EnrollmentStatusEffectiveDateStr"},
{"USER_Enrollment_Status_Effectiv", "EnrollmentStatusEffectiveDateStr"}
};


public override bool CanWrite => false;


public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{
throw new NotImplementedException();
}


public override bool CanConvert(Type objectType)
{
return objectType.GetTypeInfo().IsClass;
}


public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
object instance = Activator.CreateInstance(objectType);
var props = objectType.GetTypeInfo().DeclaredProperties.ToList();


JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
if (!_propertyMappings.TryGetValue(jp.Name, out var name))
name = jp.Name;


PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name);


prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}


return instance;
}
}


[JsonConverter(typeof(MediCalFFSPhysicianConverter))]
public class MediCalFFSPhysician
{
[JsonProperty(PropertyName = "EnrollmentStatusEffectiveDateStr")]
public string EnrollmentStatusEffectiveDateStr { get; set; }
}


public static class ObjectExtensions
{
public static string Dump(this object obj)
{
try
{
return System.Text.Json.JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true });
}
catch (Exception)
{
return string.Empty;
}
}
}

The output is this:

enter image description here

Adapted from: Deserializing different JSON structures to the same C# class