.NET NewtonSoft JSON десериализует карту для другого имени свойства
У меня есть строка JSON, которая получена от внешней стороны.
{
"team":[
{
"v1":"",
"attributes":{
"eighty_min_score":"",
"home_or_away":"home",
"score":"22",
"team_id":"500"
}
},
{
"v1":"",
"attributes":{
"eighty_min_score":"",
"home_or_away":"away",
"score":"30",
"team_id":"600"
}
}
]
}
Мои классы сопоставления:
public class Attributes
{
public string eighty_min_score { get; set; }
public string home_or_away { get; set; }
public string score { get; set; }
public string team_id { get; set; }
}
public class Team
{
public string v1 { get; set; }
public Attributes attributes { get; set; }
}
public class RootObject
{
public List<Team> team { get; set; }
}
Вопрос в том, что мне не нравится "класс атрибута" и "имя поля атрибутов" в классе Team. Вместо этого я хочу, чтобы его называли "TeamScore", а также удаляли "_" из имен полей и указывали имена.
JsonConvert.DeserializeObject<RootObject>(jsonText);
Я могу изменить класс "Атрибут" на "TeamScore", но если я изменю имя подачи (атрибуты в классе команды), оно не будет десериализоваться правильно и даст мне значение null. Как я могу это преодолеть?
Ответы
Ответ 1
Json.NET имеет JsonPropertyAttribute
, который позволяет вам указать имя свойства JSON, поэтому ваш код должен быть:
public class TeamScore
{
[JsonProperty("eighty_min_score")]
public string EightyMinScore { get; set; }
[JsonProperty("home_or_away")]
public string HomeOrAway { get; set; }
[JsonProperty("score ")]
public string Score { get; set; }
[JsonProperty("team_id")]
public string TeamId { get; set; }
}
public class Team
{
public string v1 { get; set; }
[JsonProperty("attributes")]
public TeamScore TeamScores { get; set; }
}
public class RootObject
{
public List<Team> Team { get; set; }
}
Документация: Атрибуты сериализации
Ответ 2
Если вы хотите использовать динамическое сопоставление и не хотите загромождать свою модель атрибутами, этот подход работал у меня
Использование:
var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new CustomContractResolver();
this.DataContext = JsonConvert.DeserializeObject<CountResponse>(jsonString, settings);
Логика:
public class CustomContractResolver : DefaultContractResolver
{
private Dictionary<string, string> PropertyMappings { get; set; }
public CustomContractResolver()
{
this.PropertyMappings = new Dictionary<string, string>
{
{"Meta", "meta"},
{"LastUpdated", "last_updated"},
{"Disclaimer", "disclaimer"},
{"License", "license"},
{"CountResults", "results"},
{"Term", "term"},
{"Count", "count"},
};
}
protected override string ResolvePropertyName(string propertyName)
{
string resolvedName = null;
var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
}
}
Ответ 3
Добавление к решению Jacks. Мне нужно Deserialize, используя JsonProperty и Serialize, игнорируя JsonProperty (или наоборот). ReflectionHelper и Attribute Helper - это только вспомогательные классы, которые получают список свойств или атрибутов для свойства. Я могу включить, если кто-то действительно заботится. Используя приведенный ниже пример, вы можете сериализовать viewmodel и получить "Amount", даже если JsonProperty - "RecurringPrice".
/// <summary>
/// Ignore the Json Property attribute. This is usefule when you want to serialize or deserialize differently and not
/// let the JsonProperty control everything.
/// </summary>
/// <typeparam name="T"></typeparam>
public class IgnoreJsonPropertyResolver<T> : DefaultContractResolver
{
private Dictionary<string, string> PropertyMappings { get; set; }
public IgnoreJsonPropertyResolver()
{
this.PropertyMappings = new Dictionary<string, string>();
var properties = ReflectionHelper<T>.GetGetProperties(false)();
foreach (var propertyInfo in properties)
{
var jsonProperty = AttributeHelper.GetAttribute<JsonPropertyAttribute>(propertyInfo);
if (jsonProperty != null)
{
PropertyMappings.Add(jsonProperty.PropertyName, propertyInfo.Name);
}
}
}
protected override string ResolvePropertyName(string propertyName)
{
string resolvedName = null;
var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
}
}
Использование:
var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new IgnoreJsonPropertyResolver<PlanViewModel>();
var model = new PlanViewModel() {Amount = 100};
var strModel = JsonConvert.SerializeObject(model,settings);
Модель:
public class PlanViewModel
{
/// <summary>
/// The customer is charged an amount over an interval for the subscription.
/// </summary>
[JsonProperty(PropertyName = "RecurringPrice")]
public double Amount { get; set; }
/// <summary>
/// Indicates the number of intervals between each billing. If interval=2, the customer would be billed every two
/// months or years depending on the value for interval_unit.
/// </summary>
public int Interval { get; set; } = 1;
/// <summary>
/// Number of free trial days that can be granted when a customer is subscribed to this plan.
/// </summary>
public int TrialPeriod { get; set; } = 30;
/// <summary>
/// This indicates a one-time fee charged upfront while creating a subscription for this plan.
/// </summary>
[JsonProperty(PropertyName = "SetupFee")]
public double SetupAmount { get; set; } = 0;
/// <summary>
/// String representing the type id, usually a lookup value, for the record.
/// </summary>
[JsonProperty(PropertyName = "TypeId")]
public string Type { get; set; }
/// <summary>
/// Billing Frequency
/// </summary>
[JsonProperty(PropertyName = "BillingFrequency")]
public string Period { get; set; }
/// <summary>
/// String representing the type id, usually a lookup value, for the record.
/// </summary>
[JsonProperty(PropertyName = "PlanUseType")]
public string Purpose { get; set; }
}
Ответ 4
Расширение ответа Rentering.com в сценариях, где нужно позаботиться о целом графике многих типов, и вы ищете строго типизированное решение, этот класс может помочь, см. использование (свободно) ниже. Он работает как черный список или белый список для каждого типа. Тип не может быть (Gist - также содержит глобальный список игнорирования).
public class PropertyFilterResolver : DefaultContractResolver
{
const string _Err = "A type can be either in the include list or the ignore list.";
Dictionary<Type, IEnumerable<string>> _IgnorePropertiesMap = new Dictionary<Type, IEnumerable<string>>();
Dictionary<Type, IEnumerable<string>> _IncludePropertiesMap = new Dictionary<Type, IEnumerable<string>>();
public PropertyFilterResolver SetIgnoredProperties<T>(params Expression<Func<T, object>>[] propertyAccessors)
{
if (propertyAccessors == null) return this;
if (_IncludePropertiesMap.ContainsKey(typeof(T))) throw new ArgumentException(_Err);
var properties = propertyAccessors.Select(GetPropertyName);
_IgnorePropertiesMap[typeof(T)] = properties.ToArray();
return this;
}
public PropertyFilterResolver SetIncludedProperties<T>(params Expression<Func<T, object>>[] propertyAccessors)
{
if (propertyAccessors == null)
return this;
if (_IgnorePropertiesMap.ContainsKey(typeof(T))) throw new ArgumentException(_Err);
var properties = propertyAccessors.Select(GetPropertyName);
_IncludePropertiesMap[typeof(T)] = properties.ToArray();
return this;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
var isIgnoreList = _IgnorePropertiesMap.TryGetValue(type, out IEnumerable<string> map);
if (!isIgnoreList && !_IncludePropertiesMap.TryGetValue(type, out map))
return properties;
Func<JsonProperty, bool> predicate = jp => map.Contains(jp.PropertyName) == !isIgnoreList;
return properties.Where(predicate).ToArray();
}
string GetPropertyName<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
if (!(propertyLambda.Body is MemberExpression member))
throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");
if (!(member.Member is PropertyInfo propInfo))
throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");
var type = typeof(TSource);
if (!type.GetTypeInfo().IsAssignableFrom(propInfo.DeclaringType.GetTypeInfo()))
throw new ArgumentException($"Expresion '{propertyLambda}' refers to a property that is not from type '{type}'.");
return propInfo.Name;
}
}
Использование:
var resolver = new PropertyFilterResolver()
.SetIncludedProperties<User>(
u => u.Id,
u => u.UnitId)
.SetIgnoredProperties<Person>(
r => r.Responders)
.SetIncludedProperties<Blog>(
b => b.Id)
.Ignore(nameof(IChangeTracking.IsChanged)); //see gist