可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have following JSON string which is received from an external party.
{
\"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\"
}
}
]
}
My mapping classes:
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; }
}
The question is that I don\'t like the \"Attribute class\" and the \"attributes field name\" in the Team class. Instead, I want it to be named as \"TeamScore\" and also to remove \"_\" from the field names and give proper names.
JsonConvert.DeserializeObject<RootObject>(jsonText);
I can change the \"Attribute\" class to a \"TeamScore\", but if I change the filed name (attributes in the Team Class), it won\'t deserialize properly and gives me null. How can I overcome this?
回答1:
Json.NET has a JsonPropertyAttribute
which allows you to specify the name of a JSON property, so your code should be:
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; }
}
Documentation: Serialization Attributes
回答2:
If you\'d like to use dynamic mapping, and don\'t want to clutter up your model with attributes, this approach worked for me
Usage:
var settings = new JsonSerializerSettings();
settings.DateFormatString = \"YYYY-MM-DD\";
settings.ContractResolver = new CustomContractResolver();
this.DataContext = JsonConvert.DeserializeObject<CountResponse>(jsonString, settings);
Logic:
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:
Adding to Jacks solution. I need to Deserialize using the JsonProperty and Serialize while ignoring the JsonProperty (or vice versa). ReflectionHelper and Attribute Helper are just helper classes that get a list of properties or attributes for a property. I can include if anyone actually cares. Using the example below you can serialize the viewmodel and get \"Amount\" even though the JsonProperty is \"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);
}
}
Usage:
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);
Model:
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:
Expanding Rentering.com\'s answer, in scenarios where a whole graph of many types is to be taken care of, and you\'re looking for a strongly typed solution, this class can help, see usage (fluent) below. It operates as either a black-list or white-list per type. A type cannot be both (Gist - also contains global ignore list).
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;
}
}
Usage:
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