.NET NewtonSoft JSON deserialize map to a different property name

后端 未结 5 663
情歌与酒
情歌与酒 2020-11-22 09:20

I have following JSON string which is received from an external party.

{
   \"team\":[
      {
         \"v1\":\"\",
         \"attributes\":{
            \"         


        
相关标签:
5条回答
  • 2020-11-22 09:36

    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
    
    0 讨论(0)
  • 2020-11-22 09:37

    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

    0 讨论(0)
  • 2020-11-22 09:42

    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; }
    }
    
    0 讨论(0)
  • 2020-11-22 09:43

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 09:44

    I am using JsonProperty attributes when serializing but ignoring them when deserializing using this ContractResolver:

    public class IgnoreJsonPropertyContractResolver: DefaultContractResolver
        {
            protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
            {
                var properties = base.CreateProperties(type, memberSerialization);
                foreach (var p in properties) { p.PropertyName = p.UnderlyingName; }
                return properties;
            }
        }
    

    The ContractResolver just sets every property back to the class property name (simplified from Shimmy's solution). Usage:

    var airplane= JsonConvert.DeserializeObject<Airplane>(json, 
        new JsonSerializerSettings { ContractResolver = new IgnoreJsonPropertyContractResolver() });
    
    0 讨论(0)
提交回复
热议问题