【问题标题】:NewtonSoft add JSONIGNORE at runTimeNewtonSoft 在运行时添加 JSONIGNORE
【发布时间】:2014-08-06 10:04:01
【问题描述】:

我希望使用 NewtonSoft JSON 对列表进行序列化,我需要在序列化时忽略其中一个属性,我得到了以下代码

public class Car
{
  // included in JSON
  public string Model { get; set; }
  // ignored
  [JsonIgnore]
  public DateTime LastModified { get; set; }
}

但是我在我的应用程序的许多地方都使用了这个特定的类 Car,我只想在一个地方排除该选项。

我可以在我需要的特定位置动态添加 [JsonIgnore] 吗?我该怎么做?

【问题讨论】:

    标签: c# attributes json.net


    【解决方案1】:

    不需要做其他答案中解释的复杂事情。

    NewtonSoft JSON 有一个内置功能:

    public bool ShouldSerializeINSERT_YOUR_PROPERTY_NAME_HERE()
    {
        if(someCondition){
            return true;
        }else{
            return false;
        }
    }
    

    它被称为“条件属性序列化”和文档can be found here

    警告:首先,删除{get;set;} 属性上方的[JsonIgnore] 很重要。否则它将覆盖ShouldSerializeXYZ 行为。

    【讨论】:

    • 这确实应该被标记为答案。
    • 这仅在您可以计算模型类内部的条件值时才有效,这在您必须在模型外部指定忽略策略的情况下不是很有用,即在特定的 API 方法中不需要返回所有数据。
    • @fingust 你不能在类上有一个属性,其中包含一个不序列化的属性列表。然后在调用序列化之前锁定并设置此属性中的值,并在后清除它序列化使其运行时可管理?
    【解决方案2】:

    我认为最好使用自定义 IContractResolver 来实现:

    public class DynamicContractResolver : DefaultContractResolver
    {
        private readonly string _propertyNameToExclude;
    
        public DynamicContractResolver(string propertyNameToExclude)
        {
            _propertyNameToExclude = propertyNameToExclude;
        }
    
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
    
            // only serializer properties that are not named after the specified property.
            properties =
                properties.Where(p => string.Compare(p.PropertyName, _propertyNameToExclude, true) != 0).ToList();
    
            return properties;
        }
    }
    

    LINQ 可能不正确,我没有机会对此进行测试。然后您可以按如下方式使用它:

    string json = JsonConvert.SerializeObject(car, Formatting.Indented,
       new JsonSerializerSettings { ContractResolver = new DynamicContractResolver("LastModified") });
    

    请参阅the documentation 了解更多信息。

    【讨论】:

    • 基于这篇文章,我创建了一个要在序列化时排除的属性列表。查看我上面的反馈。
    • 不分配的更好方法是为属性设置ignored = true
    【解决方案3】:

    根据上面的@Underscore 帖子,我创建了一个要在序列化时排除的属性列表。

    public class DynamicContractResolver : DefaultContractResolver {
        private readonly string[] props;
    
        public DynamicContractResolver(params string[] prop) {
            this.props = prop;
        }
    
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) {
            IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
    
            // return all the properties which are not in the ignore list
            retval = retval.Where(p => !this.props.Contains(p.PropertyName)).ToList();
    
            return retval;
        }
    }
    

    用途:

    string json = JsonConvert.SerializeObject(car, Formatting.Indented, 
        new JsonSerializerSettings { ContractResolver = new DynamicContractResolver("ID", "CreatedAt", "LastModified") });
    

    【讨论】:

      【解决方案4】:

      通过引用Dynamically rename or ignore properties without changing the serialized class,我们可以在运行时实现JsonIgnore。这是一个可行的解决方案。

      以 Person 类为例:

      public class Person
      {
          // ignore property
          [JsonIgnore]
          public string Title { get; set; }
      
      // rename property
      [JsonProperty("firstName")]
      public string FirstName { get; set; }
      }
      

      第 1 步:创建类“PropertyRenameAndIgnoreSerializerContractResolver”

      public class PropertyRenameAndIgnoreSerializerContractResolver : DefaultContractResolver
      {
          private readonly Dictionary<Type, HashSet<string>> _ignores;
          private readonly Dictionary<Type, Dictionary<string, string>> _renames;
      
      public PropertyRenameAndIgnoreSerializerContractResolver()
      {
          _ignores = new Dictionary<Type, HashSet<string>>();
          _renames = new Dictionary<Type, Dictionary<string, string>>();
      }
      
      public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
      {
          if (!_ignores.ContainsKey(type))
              _ignores[type] = new HashSet<string>();
      
          foreach (var prop in jsonPropertyNames)
              _ignores[type].Add(prop);
      }
      
      public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
      {
          if (!_renames.ContainsKey(type))
              _renames[type] = new Dictionary<string, string>();
      
          _renames[type][propertyName] = newJsonPropertyName;
      }
      
      protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
      {
          var property = base.CreateProperty(member, memberSerialization);
      
          if (IsIgnored(property.DeclaringType, property.PropertyName))
          {
              property.ShouldSerialize = i => false;
              property.Ignored = true;
          }
      
          if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
              property.PropertyName = newJsonPropertyName;
      
          return property;
      }
      
      private bool IsIgnored(Type type, string jsonPropertyName)
      {
          if (!_ignores.ContainsKey(type))
              return false;
      
          return _ignores[type].Contains(jsonPropertyName);
      }
      
      private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
      {
          Dictionary<string, string> renames;
      
          if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
          {
              newJsonPropertyName = null;
              return false;
          }
      
          return true;
      }
      }
      

      第 2 步:在 Jsonignore 想要应用的方法中添加代码

      var person = new Person();
      var jsonResolver = new PropertyRenameAndIgnoreSerializerContractResolver();
      
      jsonResolver.IgnoreProperty(typeof(Person), "Title");
      jsonResolver.RenameProperty(typeof(Person), "FirstName", "firstName");
      
      var serializerSettings = new JsonSerializerSettings();
      serializerSettings.ContractResolver = jsonResolver;
      
      var json = JsonConvert.SerializeObject(person, serializerSettings);
      

      【讨论】:

        【解决方案5】:

        试试这个:

            public static void IgnoreProperty<T, TR>(this T parameter, Expression<Func<T, TR>> propertyLambda)
            {
                var parameterType = parameter.GetType();
                var propertyName = propertyLambda.GetReturnedPropertyName();
                if (propertyName == null)
                {
                    return;
                }
        
                var jsonPropertyAttribute = parameterType.GetProperty(propertyName).GetCustomAttribute<JsonPropertyAttribute>();
                jsonPropertyAttribute.DefaultValueHandling = DefaultValueHandling.Ignore;
            }
        
            public static string GetReturnedPropertyName<T, TR>(this Expression<Func<T, TR>> propertyLambda)
            {
                var member = propertyLambda.Body as MemberExpression;
                var memberPropertyInfo = member?.Member as PropertyInfo;
                return memberPropertyInfo?.Name;
            }
        

        所以你可以这样做:

        carObject.IgnoreProperty(so => so.LastModified);
        

        【讨论】:

        • GetReturnedPropertyName() 定义在哪里?
        • 这有点没用,因为您必须已经在要设置为忽略的属性上添加了JsonPropertyAttribute。在这种情况下,您不妨直接设置DefaultValueHandling 。如果没有,GetCustomAttribute() 会返回 null,并且您会在下一行得到 NullReferenceException
        【解决方案6】:

        根据接受的答案,它会是:

        [JsonIgnore]
        public bool JsonIgnore { get; set; }
        
        public bool ImageModified { get; set; }
        
        public bool ShouldSerializeImageModified() => !JsonIgnore;
        

        只要将JsonIgnore 设置为true,就意味着ImageModified 不会被序列化,并且JsonIgnore 会因为[JsonIgnore] 而被忽略。

        如果需要以这种方式编写代码,则可能表明设计不佳。系统中可能需要有 DTO 或 ViewModel,除非您想动态禁用/启用某些属性的序列化。

        【讨论】:

          【解决方案7】:

          关于所有正确答案,我想补充一点。当您有同名的嵌套属性时,忽略将影响所有同名的属性。如果您想忽略特定属性,可以执行以下操作:

              public class DynamicContractResolver : DefaultContractResolver
              {
                  private readonly string[] props;
          
                  public DynamicContractResolver(params string[] prop)
                  {
                     this.props = prop;
                  }
          
                  protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
                  {
                     IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
                     return retval.Where(p => !this.props.Contains(p.DeclaringType.FullName + "." + p.PropertyName)).ToList();
                  }
              }
          

          那么当你想使用它的时候你可以说:

          var values = await _dbContext
                          .Set<EntityName>()
                          .Where(...).ToList();
          
          
                      var json = JsonConvert.SerializeObject(values, Formatting.Indented,
                              new JsonSerializerSettings
                              {
                                  ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                                  ContractResolver = new DynamicContractResolver("Entities.Contact.Address1","Entities.User.Name","Entities.Event.Name")
                              });
          

          Address1 将在 Contact 中被忽略,而不是在其他任何地方。

          【讨论】:

            猜你喜欢
            • 2017-12-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-11-02
            相关资源
            最近更新 更多