363

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 Attributes class name and the attributes field names in the Team class. Instead, I want it to be named TeamScore and also to remove _ from the field names and give proper names.

JsonConvert.DeserializeObject<RootObject>(jsonText);

I can rename Attributes to TeamScore, but if I change the field name (attributes in the Team class), it won't deserialize properly and gives me null. How can I overcome this?

Pierre Arnaud
  • 10,212
  • 11
  • 77
  • 108
RasikaSam
  • 5,363
  • 6
  • 28
  • 36
  • Related post - [How can I change property names when serializing with Json.net?](https://stackoverflow.com/q/8796618/465053) – RBT Sep 06 '19 at 05:56

6 Answers6

713

Json.NET - Newtonsoft 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

DecimalTurn
  • 3,243
  • 3
  • 16
  • 36
outcoldman
  • 11,584
  • 2
  • 26
  • 30
  • 5
    can i use two JsonProperty for one filed? – Ali Yousefi May 11 '15 at 19:56
  • 2
    @AliYousefie Don't think so. But the good question will be, what do you expect to get from that? – outcoldman Jul 06 '15 at 20:02
  • 6
    i have an interface, two classes are used this interface,but server data have two property name for two classes, i want use two JsonProperty for one property in my interfaces. – Ali Yousefi Apr 07 '16 at 06:35
  • how can we make sure to have the response [deserilized object] have value for EightyMinScore and not eighty_min_score – Gaurravs Jan 10 '19 at 11:17
  • In my case I am sending the RootObject as final response, but when I read it as json from the final response, eighty_min_score is shown with value and not with EightyMinScore – Gaurravs Jan 10 '19 at 11:18
  • 1
    I need to read data from an API and deserialize it to my model. Then send that model as the result of my action. Is there any way to say deserialize data from a property (Let's say `foo_bar`) but serialize to another name (Let's say `foo`). – Hamid Mayeli Feb 13 '20 at 21:40
  • I down voted because this response is specific to a completely different library other than the library that the user is asking about. This user is asking specifically how to do this in NewtonSoft, not Json.Net. – Camille Sévigny May 28 '20 at 23:39
  • 2
    @CamilleSévigny FYI they both are the same. NewtonSoft was formerly known as Json.NET. The library's author's name is "James Newton-King", hence NewtonSoft. Follow the link provided by outcoldman at the start of the answer and check their github project, it points to: https://github.com/JamesNK/Newtonsoft.Json – Khurram Hassan Nov 08 '21 at 02:53
150

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);
    }
}
Jack
  • 2,503
  • 1
  • 21
  • 15
  • 3
    Did simplify it a bit for my purpose but this is a better solution then "clutter up you model/domain" ;) – Andreas Jul 30 '15 at 08:44
  • 8
    Wow. That's epic; much more architecturally sound manner of doing it. – David Betz Dec 11 '15 at 02:53
  • 1
    It might (if you create more than one of these) be worth moving the dictionary, lookup code up to a base class for all property mappings and let them add properties but ignore the details of how the mapping happens. Might be worth just adding that to Json.Net itself. – James White Dec 30 '15 at 14:32
  • This should be the acceptable answer because, as @DavidBetz said, it's the best design. – mellis481 Jan 29 '16 at 14:46
  • Does this solution work with nested properties as well? I tried to deserialize an object with nested properties and it doesn't work. – Avi K. Jun 29 '16 at 09:54
  • You will need to override the "createContract" (I think it's called) method to be notified when a new type is being serialized and change your mapping accordingly. – Jack Jun 30 '16 at 12:33
  • Is there a way like the above to ignore properties? – Shimmy Weitzhandler May 12 '17 at 00:20
  • I like this solution however, it should be noted that you still need to decorate your class properties with `[JsonProperty]`. – Christopher Dunn Dec 10 '18 at 22:41
  • 1
    @ChristopherDunn - i got this working without having to do this - there are no json.net attrs on any of my DTOs that i needed some flexibility around when deserializing. – IbrarMumtaz Oct 29 '19 at 14:27
  • Yes, indeed. That is much better messing arround with attributes. – Saulius Oct 26 '21 at 13:57
9

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; }
}
Rentering.com
  • 399
  • 3
  • 9
  • 2
    Thanks for your IgnoreJsonPropertyResolver, since I was looking to do the same thing (ignore JsonProperty on serialization only). Unfortunately your solution only works for top level attributes and not nested types. The proper way to ignore all JsonProperty attributes when serializing is to override `CreateProperty` in the ContractResolver. There call the base: `var jsonProperty = base.CreateProperty(memberInfo, memberSerialization);` and then set `jsonProperty.PropertyName = memberInfo.Name;`. Finally `return jsonProperty;` That's all you need. – Nate Cook Dec 18 '15 at 18:40
  • 1
    what are these helpers? – Hassan Faghihi Jul 24 '16 at 13:11
  • 1
    @NateCook can you show me a sample? i need it badly right now – Hassan Faghihi Jul 24 '16 at 13:14
6

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
Community
  • 1
  • 1
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
5

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() });
Dana
  • 634
  • 7
  • 12
1

Also if you want to ignore something use this

  [JsonIgnore]
  public int Id { get; set; }
  [JsonProperty("id")]
  Public string real_id { get; set; }