.NET NewtonSoft JSON 反序列化映射到不同的属性名称

Posted

技术标签:

【中文标题】.NET NewtonSoft JSON 反序列化映射到不同的属性名称【英文标题】:.NET NewtonSoft JSON deserialize map to a different property name 【发布时间】:2013-04-01 16:24:33 【问题描述】:

我有以下从外部方收到的 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; 

问题是我不喜欢Attributes 类名Team 类中的attributes 字段名。相反,我希望将其命名为 TeamScore 并从字段名称中删除 _ 并给出正确的名称。

JsonConvert.DeserializeObject<RootObject>(jsonText);

我可以将Attributes 重命名为TeamScore,但如果我更改字段名称(Team 类中的attributes),它将无法正确反序列化并给我null。我该如何克服这个问题?

【问题讨论】:

相关帖子 - How can I change property names when serializing with Json.net? 【参考方案1】:

Json.NET - Newtonsoft 有一个 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; 

文档:Serialization Attributes

【讨论】:

我可以为一个文件使用两个 JsonProperty 吗? @AliYousefie 不要这么认为。但好问题是,您希望从中得到什么? 我有一个接口,这个接口使用了两个类,但是服务器数据有两个类的两个属性名称,我想在我的接口中为一个属性使用两个 JsonProperty。 我们如何确保响应 [deserilized object] 具有 EightyMinScore 而不是 Eighty_min_score 的值 @CamilleSévigny 仅供参考,它们都是一样的。 NewtonSoft 以前称为 Json.NET。图书馆作者的名字是“James Newton-King”,因此是 NewtonSoft。按照答案开头 outcoldman 提供的链接并检查他们的 github 项目,它指向:github.com/JamesNK/Newtonsoft.Json【参考方案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);
    

【讨论】:

出于我的目的确实简化了一点,但这是一个更好的解决方案,然后“让你的模型/域混乱”;) 哇。那是史诗;这样做的方式在架构上更合理。 可能(如果您创建多个)值得移动字典,将代码查找到所有属性映射的基类并让它们添加属性但忽略映射方式的细节发生。可能值得将其添加到 Json.Net 本身。 这应该是可以接受的答案,因为正如@DavidBetz 所说,这是最好的设计。 此解决方案是否也适用于嵌套属性?我试图反序列化具有嵌套属性的对象,但它不起作用。【参考方案3】:

添加到 Jacks 解决方案。我需要使用 JsonProperty 和 Serialize 反序列化,同时忽略 JsonProperty(反之亦然)。 ReflectionHelper 和 Attribute Helper 只是帮助类,它们获取属性的属性或属性列表。如果有人真正关心,我可以包括在内。使用下面的示例,即使 JsonProperty 是“RecurringPrice”,您也可以序列化视图模型并获得“Amount”。

    /// <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; 

【讨论】:

感谢您的 IgnoreJsonPropertyResolver,因为我想做同样的事情(仅在序列化时忽略 JsonProperty)。不幸的是,您的解决方案仅适用于***属性,不适用于嵌套类型。序列化时忽略所有 JsonProperty 属性的正确方法是覆盖 ContractResolver 中的CreateProperty。那里调用基地:var jsonProperty = base.CreateProperty(memberInfo, memberSerialization);,然后设置jsonProperty.PropertyName = memberInfo.Name;。最后return jsonProperty; 这就是你所需要的。 这些助手是什么? @NateCook 你能给我看个样品吗?我现在非常需要它【参考方案4】:

扩展Rentering.com's 答案,在需要处理多种类型的整个图并且您正在寻找强类型解决方案的情况下,此类可以提供帮助,请参阅下面的用法(流畅)。它作为每种类型的黑名单或白名单运行。一个类型不能两者兼有(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

【讨论】:

【参考方案5】:

我在序列化时使用 JsonProperty 属性,但在使用 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;
        
    

ContractResolver 只是将每个属性设置回类属性名称(简化自 Shimmy 的解决方案)。用法:

var airplane= JsonConvert.DeserializeObject<Airplane>(json, 
    new JsonSerializerSettings  ContractResolver = new IgnoreJsonPropertyContractResolver() );

【讨论】:

以上是关于.NET NewtonSoft JSON 反序列化映射到不同的属性名称的主要内容,如果未能解决你的问题,请参考以下文章

csharp 使用Newtonsoft JSON.NET将任何对象序列化/反序列化为JSON

.Net使用Newtonsoft.Json.dll(JSON.NET)对象序列化成json反序列化json示例教程

一:Newtonsoft.Json 支持序列化与反序列化的.net 对象类型;

如何使用 NewtonSoft Json.Net 将 Json 字典反序列化为平面类

使用 Newtonsoft 将 JSON 反序列化为 .NET 对象(或者可能是 LINQ to JSON?)

.NET NewtonSoft JSON 反序列化映射到不同的属性名称