Serialize only interface properties to JSON with Json.net

前端 未结 9 1947
孤街浪徒
孤街浪徒 2020-12-08 19:50

With a simple class/interface like this

public interface IThing
{
    string Name { get; set; }
}

public class Thing : IThing
{
    public int Id { get; se         


        
相关标签:
9条回答
  • 2020-12-08 20:10

    You can use conditional serialization. Take a look at this link. Basicly, you need to implement the IContractResolver interface, overload the ShouldSerialize method and pass your resolver to the constructor of the Json Serializer.

    0 讨论(0)
  • 2020-12-08 20:11

    Improved version with nested interfaces + support for xsd.exe objects

    Yet another variation here. The code came from http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html with the following improvements over other answers here

    • Handles hierarchy, so if you have an Interface2[] within an Interface1 then it will get serialized.
    • I was trying to serialize a WCF proxy object and the resultant JSON came up as {}. Turned out all properties were set to Ignore=true so I had to add a loop to set them all to not being ignored.

      public class InterfaceContractResolver : DefaultContractResolver
      {
          private readonly Type[] _interfaceTypes;
      
          private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
      
          public InterfaceContractResolver(params Type[] interfaceTypes)
          {
              _interfaceTypes = interfaceTypes;
      
              _typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
          }
      
          protected override IList<JsonProperty> CreateProperties(
              Type type,
              MemberSerialization memberSerialization)
          {
              var typeToSerialize = _typeToSerializeMap.GetOrAdd(
                  type,
                  t => _interfaceTypes.FirstOrDefault(
                      it => it.IsAssignableFrom(t)) ?? t);
      
              var props = base.CreateProperties(typeToSerialize, memberSerialization);
      
              // mark all props as not ignored
              foreach (var prop in props)
              {
                  prop.Ignored = false;
              }
      
              return props;
          }
      }
      
    0 讨论(0)
  • 2020-12-08 20:12

    You can add the [JsonIgnore] annotation to ignore an attribute.

    0 讨论(0)
  • 2020-12-08 20:13

    An alternative to [JsonIgnore] are the [DataContract] and [DataMember] attributes. If you class is tagged with [DataContract] the serializer will only process properties tagged with the [DataMember] attribute (JsonIgnore is an "opt-out" model while DataContract is "op-in").

    [DataContract]
    public class Thing : IThing
    {
        [DataMember]
        public int Id { get; set; }
    
        public string Name { get; set; }
    }
    

    The limitation of both approaches is that they must be implemented in the class, you cannot add them to the interface definition.

    0 讨论(0)
  • 2020-12-08 20:17

    Inspired by @user3161686, here's a small modification to InterfaceContractResolver:

    public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
            return properties;
        }
    }
    
    0 讨论(0)
  • 2020-12-08 20:19

    Finally I got when it will not work... If you want to have inside another complex object it will not be properly serialized.

    So I have made version which will extract only data stored in specific assembly and for types which have the same base interface.

    So it is made as .Net Core JsonContractResolver.

    In addition to data extraction it solves:
    a) camelCase conversion before sending data to client
    b) uses top most interface from allowed scope (by assembly) c) fixes order of fields: field from most base class will be listed first and nested object will meet this rule as well.

    public class OutputJsonResolver : DefaultContractResolver
    {
        #region Static Members
        private static readonly object syncTargets = new object();
        private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>();
    
        private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly;
        #endregion
    
        #region Override Members
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            if (type.Assembly != OutputJsonResolver.CommonAssembly)
                return base.CreateProperties(type, memberSerialization);
    
            IList<JsonProperty> properties;
            if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false)
            {
                lock (OutputJsonResolver.syncTargets)
                {
                    if (OutputJsonResolver.Targets.ContainsKey(type) == false)
                    {
                        properties = this.CreateCustomProperties(type, memberSerialization);
    
                        OutputJsonResolver.Targets[type] = properties;
                    }
                }
            }
    
            return properties;
        }
        protected override string ResolvePropertyName(string propertyName)
        {
            return propertyName.ToCase(Casing.Camel);
        }
        #endregion
    
        #region Assistants
        private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization)
        {
            // Hierarchy
            IReadOnlyList<Type> types = this.GetTypes(type);
    
            // Head
            Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault();
    
            // Sources
            IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization);
    
            // Targets
            IList<JsonProperty> targets = new List<JsonProperty>(sources.Count);
    
            // Repository
            IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType);
    
            foreach (Type current in types.Reverse())
            {
                IReadOnlyPage<JsonProperty> page;
                if (repository.TryGetValue(current, out page) == true)
                    targets.AddRange(page);
            }
    
            return targets;
        }
        private IReadOnlyList<Type> GetTypes(Type type)
        {
            List<Type> types = new List<Type>();
    
            if (type.IsInterface == true)
                types.Add(type);
    
            types.AddRange(type.GetInterfaces());
    
            return types;
        }
        #endregion
    }
    
    0 讨论(0)
提交回复
热议问题