C# 通过自定义属性(json.net)从序列化中排除属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13588022/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:06:17  来源:igfitidea点击:

Exclude property from serialization via custom attribute (json.net)

c#serializationjson.netcustom-attributes

提问by Rex M

I need to be able to control how/whether certain properties on a class are serialized. The simplest case is [ScriptIgnore]. However, I only want these attributes to be honored for this one specific serialization situation I am working on - if other modules downstream in the application also want to serialize these objects, none of these attributes should get in the way.

我需要能够控制类上的某些属性如何/是否被序列化。最简单的情况是[ScriptIgnore]。但是,我只希望在我正在处理的这种特定序列化情况下使用这些属性 - 如果应用程序下游的其他模块也想序列化这些对象,则这些属性都不应妨碍。

So my thought is to use a custom attribute MyAttributeon the properties, and initialize the specific instance of JsonSerializer with a hook that knows to look for that attribute.

所以我的想法是在属性MyAttribute上使用自定义属性,并使用知道查找该属性的钩子初始化 JsonSerializer 的特定实例。

At first glance, I don't see any of the available hook points in JSON.NET will provide the PropertyInfofor the current property to do such an inspection - only the property's value. Am I missing something? Or a better way to approach this?

乍一看,我没有看到 JSON.NET 中的任何可用挂钩点会为PropertyInfo当前属性提供进行此类检查的功能 - 只有属性的值。我错过了什么吗?或者有更好的方法来解决这个问题?

采纳答案by Randolpho

You have a few options. I recommend you read the Json.Net documentation article on the subjectbefore reading below.

你有几个选择。我建议您在阅读以下内容之前阅读有关该主题的 Json.Net 文档文章

The article presents two methods:

文章介绍了两种方法:

  1. Create a method that returns a boolvalue based on a naming convention that Json.Net will follow to determine whether or not to serialize the property.
  2. Create a custom contract resolver that ignores the property.
  1. 创建一个bool基于命名约定返回值的方法,Json.Net 将遵循该约定来确定是否序列化属性。
  2. 创建一个忽略该属性的自定义合同解析器。

Of the two, I favor the latter. Skip attributes altogether -- only use them to ignore properties across all forms of serialization. Instead, create a custom contract resolver that ignores the property in question, and only use the contract resolver when you want to ignore the property, leaving other users of the class free to serialize the property or not at their own whim.

两者之中,我更倾向于后者。完全跳过属性——仅使用它们来忽略所有序列化形式的属性。相反,创建一个忽略相关属性的自定义合约解析器,并且仅在您想要忽略该属性时使用合约解析器,让该类的其他用户可以自由地序列化该属性,或者不是他们自己的心血来潮。

EditTo avoid link rot, I'm posting the code in question from the article

编辑为了避免链接腐烂,我从文章中发布了有问题的代码

public class ShouldSerializeContractResolver : DefaultContractResolver
{
   public new static readonly ShouldSerializeContractResolver Instance =
                                 new ShouldSerializeContractResolver();

   protected override JsonProperty CreateProperty( MemberInfo member,
                                    MemberSerialization memberSerialization )
   {
      JsonProperty property = base.CreateProperty( member, memberSerialization );

      if( property.DeclaringType == typeof(Employee) &&
            property.PropertyName == "Manager" )
      {
         property.ShouldSerialize = instance =>
         {
            // replace this logic with your own, probably just  
            // return false;
            Employee e = (Employee)instance;
            return e.Manager != e;
         };
      }

      return property;
   }
}

回答by drzaus

Here's a generic reusable "ignore property" resolver based on the accepted answer:

这是基于接受的答案的通用可重用“忽略属性”解析器:

/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties.  See https://stackoverflow.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
    protected readonly Dictionary<Type, HashSet<string>> Ignores;

    public IgnorableSerializerContractResolver() {
        this.Ignores = new Dictionary<Type, HashSet<string>>();
    }

    /// <summary>
    /// Explicitly ignore the given property(s) for the given type
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName">one or more properties to ignore.  Leave empty to ignore the type entirely.</param>
    public void Ignore(Type type, params string[] propertyName) {
        // start bucket if DNE
        if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();

        foreach (var prop in propertyName) {
            this.Ignores[type].Add(prop);
        }
    }

    /// <summary>
    /// Is the given property for the given type ignored?
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool IsIgnored(Type type, string propertyName) {
        if (!this.Ignores.ContainsKey(type)) return false;

        // if no properties provided, ignore the type entirely
        if (this.Ignores[type].Count == 0) return true;

        return this.Ignores[type].Contains(propertyName);
    }

    /// <summary>
    /// The decision logic goes here
    /// </summary>
    /// <param name="member"></param>
    /// <param name="memberSerialization"></param>
    /// <returns></returns>
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (this.IsIgnored(property.DeclaringType, property.PropertyName)
        // need to check basetype as well for EF -- @per comment by user576838
        || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)) {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}


And usage:

和用法:

var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };

回答by Steve Rukuts

Here is a method based on drzaus' excellent serializer contract which uses lambda expressions. Simply add it to the same class. After all, who doesn't prefer the compiler to do the checking for them?

这是一种基于 drzaus 使用 lambda 表达式的优秀序列化器合约的方法。只需将其添加到同一个类中即可。毕竟,谁不喜欢编译器为它们做检查呢?

public IgnorableSerializerContractResolver Ignore<TModel>(Expression<Func<TModel, object>> selector)
{
    MemberExpression body = selector.Body as MemberExpression;

    if (body == null)
    {
        UnaryExpression ubody = (UnaryExpression)selector.Body;
        body = ubody.Operand as MemberExpression;

        if (body == null)
        {
            throw new ArgumentException("Could not get property name", "selector");
        }
    }

    string propertyName = body.Member.Name;
    this.Ignore(typeof (TModel), propertyName);
    return this;
}

You can now ignore properties easily and fluently:

您现在可以轻松流畅地忽略属性:

contract.Ignore<Node>(node => node.NextNode)
    .Ignore<Node>(node => node.AvailableNodes);

回答by frattaro

I don't care to set the property names as strings, in case they ever change it would break my other code.

我不在乎将属性名称设置为字符串,以防它们更改会破坏我的其他代码。

I had several "view modes" on the objects I needed to serialized, so I ended up doing something like this in the contract resolver (view mode provided by constructor argument):

我在需要序列化的对象上有几个“视图模式”,所以我最终在合约解析器中做这样的事情(由构造函数参数提供的视图模式):

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
    JsonProperty property = base.CreateProperty(member, memberSerialization);
    if (viewMode == ViewModeEnum.UnregisteredCustomer && member.GetCustomAttributes(typeof(UnregisteredCustomerAttribute), true).Length == 0)
    {
        property.ShouldSerialize = instance => { return false; };
    }

    return property;
}

Where my objects look like this:

我的对象看起来像这样:

public interface IStatement
{
    [UnregisteredCustomer]
    string PolicyNumber { get; set; }

    string PlanCode { get; set; }

    PlanStatus PlanStatus { get; set; }

    [UnregisteredCustomer]
    decimal TotalAmount { get; }

    [UnregisteredCustomer]
    ICollection<IBalance> Balances { get; }

    void SetBalances(IBalance[] balances);
}

The downside to this would be the bit of reflection in the resolver, but I think it's worth it to have more maintainable code.

这样做的缺点是解析器中的一些反射,但我认为拥有更多可维护的代码是值得的。

回答by Ramón Esteban

Use the JsonIgnoreattribute.

使用JsonIgnore属性。

For example, to exclude Id:

例如,要排除Id

public class Person {
    [JsonIgnore]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

回答by Baron Ch'ng

I had good results with the combination of both drzaus and Steve Rukuts answers. However, I face a problem when I set JsonPropertyAttribute with a different name or caps for the property. For example:

结合 drzaus 和 Steve Rukuts 的答案,我取得了不错的结果。但是,当我为 JsonPropertyAttribute 设置不同的名称或属性时,我遇到了问题。例如:

[JsonProperty("username")]
public string Username { get; set; }

Include UnderlyingName into consideration solves the problem:

包括 UnderlyingName 考虑解决问题:

protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
    JsonProperty property = base.CreateProperty(member, memberSerialization);

    if (this.IsIgnored(property.DeclaringType, property.PropertyName)
        || this.IsIgnored(property.DeclaringType, property.UnderlyingName)
        || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)
        || this.IsIgnored(property.DeclaringType.BaseType, property.UnderlyingName))
    {
        property.ShouldSerialize = instance => { return false; };
    }

    return property;
}

回答by cmeeren

If you are willing to use F# (or simply use an API not optimized for C#), the FSharp.JsonSkippablelibrary allows you to control in a simple and strongly typed manner whether to include a given property when serializing (and determine whether a property was included when deserializing), and moreover, to control/determine exclusion separately of nullability. (Full disclosure: I'm the author of the library.)

如果您愿意使用 F#(或只是使用未针对 C# 优化的 API),则FSharp.JsonSkippable库允许您以简单且强类型的方式控制在序列化时是否包含给定属性(并确定属性是否为反序列化时包括在内),此外,控制/确定与可空性分开的排除。(完全披露:我是图书馆的作者。)