NewtonSoft在运行时添加JSONIGNORE

本文关键字:JSONIGNORE 添加 运行时 NewtonSoft | 更新日期: 2023-09-27 18:16:33

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

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

但是在我的应用程序中,我在很多地方使用这个特定类别的汽车,我想只在一个地方排除这个选项。

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

NewtonSoft在运行时添加JSONIGNORE

不需要做另一个答案中解释的复杂事情。

NewtonSoft JSON有一个内置功能:

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

它被称为"条件属性序列化",文档可以在这里找到。

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

我认为最好使用自定义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") });

有关更多信息,请参阅文档。

基于上面的@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") });

通过引用在不更改序列化类的情况下动态重命名或忽略属性,我们可以在运行时实现JsonIgnore。这是一个可行的解决方案。

以人员类别为例:

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

步骤1:创建类"PropertyRenameAndIgnoreSerizerContractResolver">

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);
关于所有正确答案,我想补充一些内容。如果嵌套了具有相同名称的属性,则忽略将影响所有具有相同名称属性。如果你喜欢忽略一个特定的属性,你可以这样做:
    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中被忽略,而不是在其他任何位置。

试试这个:

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

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

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

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

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