【问题标题】:Is there a way to ignore get-only properties in Json.NET without using JsonIgnore attributes?有没有办法在不使用 JsonIgnore 属性的情况下忽略 Json.NET 中的 get-only 属性?
【发布时间】:2013-08-31 01:09:41
【问题描述】:

有没有办法使用 Json.NET 序列化程序忽略 get-only 属性但不使用 JsonIgnore 属性?

例如,我有一个具有这些获取属性的类:

    public Keys Hotkey { get; set; }

    public Keys KeyCode
    {
        get
        {
            return Hotkey & Keys.KeyCode;
        }
    }

    public Keys ModifiersKeys
    {
        get
        {
            return Hotkey & Keys.Modifiers;
        }
    }

    public bool Control
    {
        get
        {
            return (Hotkey & Keys.Control) == Keys.Control;
        }
    }

    public bool Shift
    {
        get
        {
            return (Hotkey & Keys.Shift) == Keys.Shift;
        }
    }

    public bool Alt
    {
        get
        {
            return (Hotkey & Keys.Alt) == Keys.Alt;
        }
    }

    public Modifiers ModifiersEnum
    {
        get
        {
            Modifiers modifiers = Modifiers.None;

            if (Alt) modifiers |= Modifiers.Alt;
            if (Control) modifiers |= Modifiers.Control;
            if (Shift) modifiers |= Modifiers.Shift;

            return modifiers;
        }
    }

    public bool IsOnlyModifiers
    {
        get
        {
            return KeyCode == Keys.ControlKey || KeyCode == Keys.ShiftKey || KeyCode == Keys.Menu;
        }
    }

    public bool IsValidKey
    {
        get
        {
            return KeyCode != Keys.None && !IsOnlyModifiers;
        }
    }

我是否需要将[JsonIgnore] 添加到所有这些(我还有许多其他类),或者有更好的方法来忽略所有仅获取属性?

【问题讨论】:

    标签: c# json json.net


    【解决方案1】:

    您可以通过实现自定义IContractResolver 并在序列化过程中使用它来做到这一点。如果您将DefaultContractResolver 子类化,这将变得非常容易:

    class WritablePropertiesOnlyResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
            return props.Where(p => p.Writable).ToList();
        }
    }
    

    这是一个演示如何使用它的测试程序:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    
    class Program
    {
        static void Main(string[] args)
        {
            Widget w = new Widget { Id = 2, Name = "Joe Schmoe" };
    
            JsonSerializerSettings settings = new JsonSerializerSettings
            {
                ContractResolver = new WritablePropertiesOnlyResolver()
            };
    
            string json = JsonConvert.SerializeObject(w, settings);
    
            Console.WriteLine(json);
        }
    }
    
    class Widget
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string LowerCaseName
        {
            get { return (Name != null ? Name.ToLower() : null); }
        }
    }
    

    这是上面的输出。请注意,只读属性 LowerCaseName 未包含在输出中。

    {"Id":2,"Name":"Joe Schmoe"}
    

    【讨论】:

    • 只忽略不带SET的属性怎么样?并且当有像 public string Name { get; 这样的私有集时仍然序列化私人套装; } ?
    • 此解决方案存在问题,因为 C#6 中引入了 get-only 属性。
    • @KasparKallas How so?
    • @BrianRogers 如果不序列化 get-only 属性,您可能会丢失重要信息。但是......这就是OP所要求的......而您的回答提供了这一点。抱歉,我被自己的用例所困扰。我不想序列化 computed 属性。我相信这将是当今更常见的用例,但我可能是错的。
    • 我相信它和JsonSerializerOptions上的System.Text.Json的IgnoreReadOnlyProperties一样工作
    【解决方案2】:

    使用 JSON.net 的 OptIn 模式,你只需要装饰你想要序列化的属性。这不如自动选择退出所有只读属性,但它可以为您节省一些工作。

    [JsonObject(MemberSerialization.OptIn)]
    public class MyClass
    {
        [JsonProperty]
        public string serializedProp { get; set; }
    
        public string nonSerializedProp { get; set; }
    }
    

    Udate:使用反射添加了另一种可能性

    如果上述解决方案仍然不是您想要的,您可以使用反射来制作字典对象,然后将其序列化。当然,下面的示例仅适用于简单的类,因此如果您的类包含其他类,则需要添加递归。这至少应该为您指明正确的方向。

    将过滤结果放入字典的子程序:

        private Dictionary<String, object> ConvertToDictionary(object classToSerialize)
        {
            Dictionary<String, object> resultDictionary = new Dictionary<string, object>();
    
            foreach (var propertyInfo in classToSerialize.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (propertyInfo.CanWrite) resultDictionary.Add(propertyInfo.Name, propertyInfo.GetValue(classToSerialize, null));
            }
    
            return resultDictionary;
        }
    

    一个显示其用途的 sn-p:

    SampleClass sampleClass = new SampleClass();
    sampleClass.Hotkey = Keys.A;
    var toSerialize = ConvertToDictionary(sampleClass);
    String resultText = JsonConvert.SerializeObject(toSerialize);
    

    【讨论】:

      【解决方案3】:

      您可以像这样使用合同解析器:

      public class ExcludeCalculatedResolver : DefaultContractResolver
      {
          protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
          {
              var property = base.CreateProperty(member, memberSerialization);
              property.ShouldSerialize = _ => ShouldSerialize(member);
              return property;
          }
      
          internal static bool ShouldSerialize(MemberInfo memberInfo)
          {
              var propertyInfo = memberInfo as PropertyInfo;
              if (propertyInfo == null)
              {
                  return false;
              }
      
              if (propertyInfo.SetMethod != null)
              {
                  return true;
              }
      
              var getMethod = propertyInfo.GetMethod;
              return Attribute.GetCustomAttribute(getMethod, typeof(CompilerGeneratedAttribute)) != null;
          }
      }
      

      它将排除计算的属性,但包括 C#6 仅获取属性和具有 set 方法的所有属性。

      【讨论】:

      • 谢谢!这是比公认的答案更正确的解决方案。它缺少检查属性是否定义了JsonPropertyAttribute,它应该覆盖ContractResolver。使用JsonProperty.HasMemberAttribute很容易做到。
      【解决方案4】:

      Json.net 确实能够在没有属性或合同解析器的情况下有条件地序列化属性。如果您不希望您的项目依赖于 Json.net,这将特别有用。

      根据Json.net documentation

      要有条件地序列化一个属性,添加一个返回布尔值的方法 与属性同名,然后在方法名称前加上 应该序列化。该方法的结果确定是否 属性被序列化。如果方法返回 true 则属性 将被序列化,如果它返回 false 那么属性将是 跳过。

      【讨论】:

      • 谢谢你,虽然它并不理想。我不希望我的抽象需要 newtonsoft 包,我认为他们应该使用更通用的解决方案,例如 System.ComponentModel.BrowsableAttribute 或者默认情况下忽略没有设置器的属性。
      猜你喜欢
      • 1970-01-01
      • 2018-12-11
      • 2017-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多