Представьте, что у вас есть следующий класс
public class Person
{
public int Id {get; set;}
[JsonProperty("first_name")]
public string FirstName {get; set;}
[JsonProperty("last_name")]
public string LastName {get; set;}
}
У меня есть два источника JSON, которые я сейчас использую для JsonConvert.DeserializeObject<Person>(source)
. Это ставит меня в тупик. Оба источника должны иметь одинаковые имена свойств, чтобы это работало. Ради этого упражнения давайте предположим следующее для source
.
источник 1:
{
"id" : 1,
"first_name": "Jon",
"last_name": "Doe"
}
источник 2:
{
"id" : 1,
"firstName": "Jon",
"lastName": "Doe"
}
В этом сценарии второй источник не будет десериализован в объект Person
, потому что имена свойств не совпадают.
Я не уверен, как десериализовать эти два источника в один и тот же объект без необходимости создавать новый объект, чего я не хочу делать.
Десериализуйте в Dictionary<string, object>
, а затем заполните объект из него. В любом случае вам понадобится еще один набор сопоставлений между именем свойства JSON и именем свойства C#. Выбрать свой яд.
Создайте некоторые свойства прокси только для установки (которые вы просто игнорируете везде, как следует из названия):
public class Person
{
public int Id {get; set;}
[JsonProperty("firstName")]
public string FirstName {get; set;}
[JsonProperty("lastName")]
public string LastName {get; set;}
[JsonProperty("first_name")]
public string __Ignore1 { set { FirstName = value; } }
[JsonProperty("last_name")]
public string __Ignore2 { set { LastName = value; } }
}
Демонстрация скрипки здесь.
Я пытался использовать NamingStrategy, но, похоже, это можно настроить только по-другому. Я не вижу никакого вреда в этом маленьком хаке - будучи только установленными, они не сериализуются.
Это хороший трюк! Но это делает класс уродливым и может не пройти проверку кода.
Конечно, выберите свой яд. Определенно зависит от проекта, команды, кодовой базы и т. д. Например, если это всего лишь небольшое консольное приложение или служба, анализирующая одну вещь — для любого члена моей команды вполне приемлемо кодировать это эффективно, как это. Однако, если это часть большого приложения, которое должно делать это для многих типов по аналогичному шаблону - следует использовать ваш общий подход (хотя я бы сделал более общий - удаление символов и нижний регистр).
Чтобы решить эту проблему с помощью пользовательского JsonConverter
...
Реализуйте пользовательский JsonConverter
, который определяет сопоставления имен полей, которые могут быть разными...
public class PersonConverter : JsonConverter
{
private Dictionary<string, string> propertyMappings { get; set; }
public PersonConverter()
{
this.propertyMappings = new Dictionary<string, string>
{
{"firstName","first_name"},
{"lastName","last_name"},
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 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;
}
public override bool CanConvert(Type objectType)
{
return objectType.GetTypeInfo().IsClass;
}
public override bool CanWrite => false;
}
Добавьте атрибут JsonConverter
в свой класс объектов, чтобы ваш пользовательский преобразователь использовался во время десериализации...
[JsonConverter(typeof(PersonConverter))]
public class RootObject
{
[JsonProperty("first_name")]
public string FirstName{ get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
Чтобы использовать пользовательский конвертер...
string json_first_name = "{\"id\" : 1,\"first_name\": \"Jon\",\"last_name\": \"Doe\"}";
string json_firstname = "{\"id\" : 1,\"firstName\": \"Jon\",\"lastName\": \"Doe\"}";
var objFirst_Name = JsonConvert.DeserializeObject<RootObject>(json_first_name);
var objFirstName = JsonConvert.DeserializeObject<RootObject>(json_firstname);
Вам не обязательно создавать новое сопоставление. Вероятно, есть много способов сделать это, умный хак предложил @TheSoftwareJedi, вот еще одно решение:
public class Person
{
public int Id {get; set;}
//[JsonProperty("first_name")]
public string FirstName {get; set;}
//[JsonProperty("last_name")]
public string LastName {get; set;}
}
private static void PrintDeserializedObject(string obj, DefaultContractResolver resolver)
{
var person = JsonConvert.DeserializeObject<Person>(obj, new JsonSerializerSettings
{
ContractResolver = resolver,
});
Console.WriteLine("{0}.{1}.{2}", person.Id, person.FirstName, person.LastName);
}
public static void Main()
{
var firstObj = @"{
""id"" : 1,
""first_name"": ""Jon"",
""last_name"": ""Doe""
}";
var secondObj = @"{
""id"" : 1,
""firstName"": ""Jon"",
""lastName"": ""Doe""
}";
DefaultContractResolver snakeResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
};
PrintDeserializedObject(firstObj, snakeResolver);
PrintDeserializedObject(secondObj, new CamelCasePropertyNamesContractResolver());
}
Идея состоит в том, чтобы использовать ContractResolver
и NamingStrategy
. Что касается верблюжьего корпуса, то там встроенный CamelCasePropertyNamesContractResolver
. Вместо этого для случая со змеей вам нужно создать новый DefaultContractResolver
со стратегией именования, установленной на SnakeCaseNamingStrategy
.
Обратите внимание, что в этом случае вы можете избавиться от JsonPropertyAttribute
, по крайней мере, я не нашел способа переопределить их из JsonSerializerSettings
.
Вариант № 1) Создайте отдельные промежуточные объекты для десериализации двух версий JSON, а затем сопоставьте эти объекты с вашим человеком. Вариант № 2) Создайте собственный конвертер для JSON, Пользовательский конвертер JSON