12

I am exactly in the same case that this question: How do I make JSON.NET ignore object relationships?

I see the proposed solution and I know I must use a Contract Revolver, and I also see the code of the Contract Resolver, but I do not know how to use it.

  • Should I use it in the WebApiConfig.vb?
  • Should I modify my Entity Model anyway?
Carrie Kendall
  • 11,124
  • 5
  • 61
  • 81
Carlos
  • 1,638
  • 5
  • 21
  • 39
  • If you are in the same situation than me, here is my advice: forget it and change you controller to OData. It works perfectly, and there is not problem with JSON serializer. – Carlos Oct 11 '14 at 15:19
  • Carlos, my bellow solution works, may you mark it as the correct answer? – Ramin Bateni Apr 19 '17 at 17:16
  • I have marked your answer, RAM, because it has some upvotes, so it must work. But I comment this because it can be useful (if was for me). In the constructor, Configuration.LazyLoadingEnabled did the trick: public ExampleController() { db.Configuration.LazyLoadingEnabled = false; } – Carlos Apr 20 '17 at 09:52
  • Thank you. Also my answer works in both `True` & `False` values of `LazyLoadingEnabled` property. – Ramin Bateni Apr 26 '17 at 20:24

2 Answers2

44

It is a useful question and I hope this help:

A)

If you have created your models manually (without Entity Framework), mark the relation properties as virtual first.

If your models were created by EF, It has already done it for you and each Relation Property is marked as virtual, as seen below:

enter image description here

Sample class:

public class PC
{
    public int FileFolderId {get;set;}

    public virtual ICollection<string> Libs { get; set; }
    public virtual ICollection<string> Books { get; set; }
    public virtual ICollection<string> Files { get; set; }
}

B)

Those relation properties can now be ignored by the JSON serializer by using the following ContractResolver for JSON.NET:

CustomResolver:

class CustomResolver : DefaultContractResolver
{
    private readonly List<string> _namesOfVirtualPropsToKeep=new List<string>(new String[]{});

    public CustomResolver(){}

    public CustomResolver(IEnumerable<string> namesOfVirtualPropsToKeep)
    {
        this._namesOfVirtualPropsToKeep = namesOfVirtualPropsToKeep.Select(x=>x.ToLower()).ToList();
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        var propInfo = member as PropertyInfo;
        if (propInfo != null)
        {
            if (propInfo.GetMethod.IsVirtual && !propInfo.GetMethod.IsFinal
                && !_namesOfVirtualPropsToKeep.Contains(propInfo.Name.ToLower()))
            {
                prop.ShouldSerialize = obj => false;
            }
        }
        return prop;
    }
}

C)

Finally, to serialize your model easily use the above ContractResolver. Set it up like this:

// -------------------------------------------------------------------
// Serializer settings
JsonSerializerSettings settings = new JsonSerializerSettings
{
    // ContractResolver = new CustomResolver();
    // OR:
    ContractResolver = new CustomResolver(new []
    {
        nameof(PC.Libs), // keep Libs property among virtual properties
        nameof(PC.Files) // keep Files property among virtual properties
    }),
    PreserveReferencesHandling = PreserveReferencesHandling.None,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    Formatting = Formatting.Indented
};

// -------------------------------------------------------------------
// Do the serialization and output to the console
var json = JsonConvert.SerializeObject(new PC(), settings);
Console.WriteLine(json);

// -------------------------------------------------------------------
// We can see that "Books" filed is ignored in the output:
// {
//  "FileFolderId": 0,
//  "Libs": null,
//  "Files": null
// }

Now, all the navigation (relation) properties [virtual properties] will be ignored automatically except you keep some of them by determine them in your code.

Live DEMO


Thanks from @BrianRogers for his answer here.

Ramin Bateni
  • 16,499
  • 9
  • 69
  • 98
  • 3
    you are a life savior!! – Att3t Jun 18 '19 at 10:21
  • 1
    This was an excellent answer. @RAM , would you know a way to conditionally include only specific virtual collections, may via custom annotation or some other way? – Janne Dec 08 '19 at 14:37
  • @Janne, I updated my answer to support keeping certain properties with their names (although we can make a solution based-on attributes too). Also I added a link to a Live Demo. – Ramin Bateni Jan 16 '20 at 00:05
  • Thanks for sharing @RAM, I will take this to consideration in future plans. – Janne Jan 17 '20 at 15:03
  • 1
    ... just a quick notice @RAM, code example here at StackOverflow is partial as it's missing handling of _namesOfVirtualPropsToKeep (just one more condition to IF-statement), but Live DEMO contains this missing feat. Just wanted to let you know. Thanks for the solution! – Janne Jan 21 '20 at 10:24
  • @RAM , could you confirm something for me regarding this thread on virtual collections: https://stackoverflow.com/questions/11469432/entity-framework-code-first-lazy-loading ... When the models with virtual collections are fetched, but collections are never accessed anywhere before exposing 'em to custom resolver, are collection proxies ignored when using whitelisting? Basically I'd like to know if serializer *really completely ignores* virtual collections, so that EF won't try to fill 'em for nothing, as the serializer would ignore results anyway? – Janne Feb 05 '20 at 09:19
  • @Janne, I'm not sure about fetching the whitelisted virtual properties during serialization based on lazy loading. Really I turn off lazy loading in my projects. But you can use a EF profiles in a simple new application to test what will be happen exactly. [profiler1](https://stackify.com/entity-framework-profiler/), [profiler2](http://www.hibernatingrhinos.com/products/efprof) – Ramin Bateni Feb 16 '20 at 10:14
2

If you are using Newtonsoft.Json

Mark field with

Newtonsoft.Json.JsonIgnore

Instead of

System.Text.Json.Serialization.JsonIgnore
s k
  • 4,342
  • 3
  • 42
  • 61