Saturday, September 26, 2015

How to only Serialize Interface Properties with Json.NET

When you serialize an object with Newtonsoft's Json.NET it will resolve the serialization contract for the type being serialized. This means that if you want to serialize an object so that it matches one of the interfaces that it implements you will need to use a customized contract resolver.

When I first tried to do this I made a completely custom JsonConverter for the type that looked up the properties via reflection and just wrote their values out manually. Unfortunately had the side effect of bypassing all of the features the Newtonsoft provides with regard to decorating classes and customizing the serialization process for that object.

There was a good topic on Stack Overflow about this that led me to the custom contract resolver solution. However the sample implementation there is hard coded to only try to serialize one hard coded type for all serialization.

Below is an implementation (with tests) that allows you to specify a list of interfaces that you want to serialize by, and then if the object being serialized does implement that interface it will fall back on it's default contract.

InterfaceContractResolver Implementation

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);
 
        return base.CreateProperties(typeToSerialize, memberSerialization);
    }
}

Unit Tests

public interface ISample
{
    int Id { get; }
    IList<string> Names { get; }
}
 
public class Sample : ISample
{
    int ISample.Id
    {
        get { return Identity; }
    }
 
    public int Identity { get; set; }
    public IList<string> Names { get; set; }
    public bool IsValid { get; set; }
}
 
public class Example
{
    public int Id { get; set; }
    public string Names { get; set; }
    public bool IsTrue { get; set; }
}
 
public class InterfaceContractResolverTests
{
    [Fact]
    public void SerializeSample()
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new InterfaceContractResolver(typeof (ISample))
        };
 
        var sample = new Sample
        {
            Identity = 42,
            IsValid = true,
            Names = new[] {"Linq", "Sql"}
        };
 
        var sampleJson1 = JsonConvert.SerializeObject(sample);
        var sampleJson2 = JsonConvert.SerializeObject(sample, settings);
 
        Assert.Equal(
            @"{""Identity"":42,""Names"":[""Linq"",""Sql""],""IsValid"":true}",
            sampleJson1);
 
        Assert.Equal(
            @"{""Id"":42,""Names"":[""Linq"",""Sql""]}",
            sampleJson2);
    }
 
    [Fact]
    public void SerializeExample()
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new InterfaceContractResolver(typeof (ISample))
        };
 
        var example = new Example
        {
            Id = 2,
            Names = "Hello World",
            IsTrue = false
        };
 
        var exampleJson1 = JsonConvert.SerializeObject(example);
        var exampleJson2 = JsonConvert.SerializeObject(example, settings);
 
        Assert.Equal(
            @"{""Id"":2,""Names"":""Hello World"",""IsTrue"":false}",
            exampleJson1);
 
        Assert.Equal(exampleJson1, exampleJson2);
    }
}

Enjoy,
Tom

21 comments:

  1. This solution doesn't seem to work, at least with the current version of NewtonSoft.Json. I've found out that the properties in the interface are being ignored by default. If you replace the last line of CreateProperties with this, it solves the issue:

    var props = base.CreateProperties(typeToSerialize, memberSerialization);
    foreach(var prop in props)
    {
    prop.Ignored = false;
    }
    return props;

    ReplyDelete
    Replies
    1. Thanks for letting me know. I would like to go back and update this for .NET Core; if I do, then I will make sure to post a link here to the updated version.

      Delete
  2. Importance of community and the suffering economy becoming front of mind for many people today, the local market offers a place for residents to contribute directly to individuals and organizations for whom local production is paramount. The number one priority of a local market is to offer things that are homemade, handmade and homegrown. whitley residences

    ReplyDelete
  3. There are also strict laws in place to protect buyers and investors. A country which places emphasis on protecting investors will always attract investments. For property investment, there are heavy restrictions to protect people's money in the Singapore property market. nim collection

    ReplyDelete
  4. I was surfing net and fortunately came across this site and found very interesting stuff here. Its really fun to read. I enjoyed a lot. Thanks for sharing this wonderful information. el gouna white villas for sale

    ReplyDelete
  5. Building monetary models is a workmanship. The best way to further develop your specialty is to construct an assortment of monetary models across various businesses. We should attempt a model for a venture that isn't past the range of most people - a speculation property.
    Stump Removal Stockton

    ReplyDelete
  6. We should likewise pull the claimed property estimation line from the property tab (denoting the qualities in green to show that they come from an alternate sheet). nova city developers islamabad

    ReplyDelete
  7. The cutting of the storage compartment into the few areas that would then be able to be brought down with a controlled crane activity is additionally significant for the tight situation that might be available. Tree Trimming team Syracuse

    ReplyDelete
  8. So, can a simple layman person operate on a surgical table? No, right? An inexperienced and uncertified person cannot just use an axe or chainsaw to cut a tree. Albuquerque Tree Pruning

    ReplyDelete
  9. A great deal of American administration organizations get their payoffs from the help merchants who are continually conveyed to the properties. plot for sale in university town islamabad

    ReplyDelete
  10. Your expense of conveyance will likely be at least 5 pennies for each flyer under a specific conveyance sum (say 30,000).point loma homes for sale

    ReplyDelete
  11. Tajarat strives to be Pakistan's biggest real estate developer ever, guaranteeing the highest international standards, prompt execution, and lifetime customer loyalty. For further detail visit lahore smart city payment plan

    ReplyDelete
  12. As a component of this interaction, it is insightful to incorporate adjoining properties and examine them to guarantee that they affect your subject property. real estate Nigeria

    ReplyDelete
  13. Thanks for providing recent updates regarding the concern, I look forward to read more. New lenox real estate agents

    ReplyDelete
  14. I wanted to thank you for this excellent read!! I definitely loved every little bit of it. I have you bookmarked your site to check out the new stuff you post. وصلت

    ReplyDelete
  15. You made some good points there. I did a Google search about the topic and found most people will believe your blog. Feel free to visit my website; 바카라사이트

    ReplyDelete
  16. Hi there! Nice post! Please tell us when I will see a follow up! 0
    릴게임
    포커게임

    ReplyDelete
  17. We should likewise pull the possessed property estimation line from the property tab.
    cash home buyers Philadelphia

    ReplyDelete
  18. The property estimation line will just extend the worth of the property over the long run.
    https://districtrealty.com/

    ReplyDelete
  19. No big surprise, the Web has made nearby real estate a worldwide business. completerealestatepros

    ReplyDelete

Real Time Web Analytics