39

With a simple class/interface like this

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

public class Thing : IThing
{
    public int Id { get; set; }
    public string Name { get; set; }
}

How can I get the JSON string with only the "Name" property (only the properties of the underlying interface) ?

Actually, when i make that :

var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented);
Console.WriteLine(serialized);

I get the full object as JSON (Id + Name);

2
  • I know this is old, but i just had this situation and it was easier just to ignore nulls in the object. it works for numbers and dates if they are Nullable<> tho. Commented Jan 3, 2018 at 13:02
  • strangely, still an issue in 2022. Implementing "contractresolver", instead of a signature like serialize<IThing>(obj) would have been simple. Commented Mar 21, 2022 at 16:10

10 Answers 10

28

The method I use,

public class InterfaceContractResolver : DefaultContractResolver
{
    private readonly Type _InterfaceType;
    public InterfaceContractResolver (Type InterfaceType)
    {
        _InterfaceType = InterfaceType;
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        //IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
        IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
        return properties;
    }
}

// To serialize do this:
var settings = new JsonSerializerSettings() {
     ContractResolver = new InterfaceContractResolver (typeof(IThing))
};
string json = JsonConvert.SerializeObject(theObjToSerialize, settings);
                 
                            
Sign up to request clarification or add additional context in comments.

4 Comments

This, in combination with the generic proposed by @rasx is the best solution around here.
found a codeproject where someone extended this for multiple interfaces tomdupont.net/2015/09/how-to-only-serialize-interface.html
I'm glad to have found this answer, but honestly was expecting this to be some kind of built-in feature.
Would be cleaner using generics: codeshare.io/50v8D8
23

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

5 Comments

Thank you so much. I can't believe I tried all the other versions ripping my hair out then realising they wouldn't handle nested interfaces. Thank you!
This is the best solution on this page, it wants a few up votes to get it nearer the top
Someone has written a blog post about solving it in a similar way to this: tomdupont.net/2015/09/how-to-only-serialize-interface.html
I'm missing an example of how to use the InterfaceContractResolver in this answer. I'm sure that I could figure this out, but it would really improve the quality this answer if there also were an example.
good answer but MemberSerialization seems to be NewtonSoft JSON.
22

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

Comments

12

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.

2 Comments

just a quick question why not just use a DTO then mapp the entity to the dto using Serializer ?
It really should use the type/interface - instead, it is not discriminating between members. I would classify this as a bug, and then a workaround is DefaultContractResolver
10

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.

Comments

7

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

2 Comments

Is this possible to define these ignore without attributes ? In fact, i can't modify the interface or the class neither.
jsonignore and datamember syntax is basically saying you must a) have control of the source of the object and b) know beforehand what is needed. In the real world, the object comes from someone else, and is not up to you, or know ahead of time. serialize<Interface>(obj) would have been too simple?
4

I'd like to share what we ended up doing when confronted with this task. Given the OP's interface and class...

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

public class Thing : IThing
{
   public int Id { get; set; }
   public string Name { get; set; }
}

...we created a class that is the direct implementation of the interface...

public class DirectThing : IThing
{
   public string Name { get; set; }
}

Then simply serialized our Thing instance, deserialized it as a DirectThing, then Serialized it as a DirectThing:

var thing = new Thing();
JsonConvert.SerializeObject(
    JsonConvert.DeserializeObject<DirectThing>(JsonConvert.SerializeObject(thing)));

This approach can work with a long interface inheritance chain...you just need to make a direct class (DirectThing in this example) at the level of interest. No need to worry about reflection or attributes.

From a maintenance perspective, the DirectThing class is easy to maintain if you add members to IThing because the compiler will give errors if you haven't also put them in DirectThing. However, if you remove a member X from IThing and put it in Thing instead, then you'll have to remember to remove it from DirectThing or else X would be in the end result.

From a performance perspective there are three (de)serialization operations happening here instead of one, so depending on your situation you might like to evaluate the performance difference of reflector/attribute-based solutions versus this solution. In my case I was just doing this on a small scale, so I wasn't concerned about potential losses of some micro/milliseconds.

Hope that helps someone!

Comments

3

in addition to the answer given by @monrow you can use the default [DataContract] and [DataMember] have a look at this

http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size.aspx

Comments

2

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
}

Comments

1

Yet another answer based on existing one.

The tiny problem with @rasx approach is that when the serialized type contains a field of another compound type that doesn't implement the interface, you get an exception.

The reason is that the CreateProperties is called for each serialized type, including the actual serialized object type and the types of all its properties.

A workaround is to check if the type examined in the CreateProperties is in fact a type implementing the interface or any other type.

public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
{
    protected override IList<JsonProperty> CreateProperties( Type type, MemberSerialization memberSerialization )
    {
        if ( typeof( TInterface ).IsAssignableFrom( type ) )
        {
            IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
            return properties;
        }
        else
        {
            return base.CreateProperties( type, memberSerialization );
        }
    }
}

Take a look at previously failing example that works now:

public interface IFoo
{
    string Bar { get; set; }

    Child Child { get; set; }
}

// this is the serialized type, it implements the interface
public class Foo : IFoo
{
    public string Bar { get; set; }
    public string Qux { get; set; }
    public Child Child { get; set; }
}

// this is a child type, doesn't implement the interface
public class Child
{
    public string Name { get; set; }
}

This works now

var foo = new Foo()
{
    Bar = "bar",
    Qux = "qux",
    Child = new Child()
    {
        Name = "child"
    }
};

// To serialize do this:
var settings = new JsonSerializerSettings()
{
    ContractResolver = new InterfaceContractResolver<IFoo>()
};

var s_foo = JsonConvert.SerializeObject(foo, settings);
var s_full = JsonConvert.SerializeObject(foo);

var foo_full = JsonConvert.DeserializeObject<Foo>(s_full);
var foo_foo = JsonConvert.DeserializeObject<Foo>(s_full, settings);

Console.WriteLine( foo_full );
Console.WriteLine( foo_foo );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.