0

I have two classes:

public class Customer
{
    public int ID { get; set; }

    public string Name { get; set; }

    public virtual ICollection<Order> Orders { get; set; }
}

public class Order
{
    public int ID { get; set; }

    public int CustomerID { get; set; }
    public virtual Customer Customer { get; set; }

    public string Description { get; set; }
}

I am trying to serialize a Customer to JSON using Json.Net and a ContractResolver, excluding the Customer property from every Order. I just want to keep the Description. I don't want to use Annotation like [JsonIgnore] because I want to make the serialization different for different cases. So I do something like this:

public class CustomerContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        if (property.DeclaringType == typeof(Order) && property.PropertyName.Equals(nameof(Order.Customer)))
        {
            property.ShouldSerialize = instance => { return false; };
        }
        return property;
    }
}

Then I get a customer from the database and try to serialize it:

JsonSerializerSettings settings = new JsonSerializerSettings { ContractResolver = new CustomerContractResolver() };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(customer, Formatting.Indented, settings);

The CreateProperty method of the CustomerContractResolver is called as expected for each Orderin the Orders collection, but I still get an error:

Self referencing loop detected for property 'Customer' with type 'System.Data.Entity.DynamicProxies.Customer_04661D0875E326DF9B5D4D2BA503FC19E2C0EBD49A7BE593D66F67AE77F7FDBE'. Path 'Orders[0]'.

Meaning that the ShouldSerialize delegate didn't work.

Any ideas here how to exclude the Customer property from each Order?

dbc
  • 104,963
  • 20
  • 228
  • 340
Kostas Dafnomilis
  • 629
  • 1
  • 5
  • 13
  • Can't reproduce with pure POCOs, see https://dotnetfiddle.net/4SVQXS. It looks like you may be working with entity framework, What happens if you disable dynamic proxies as shown [here](https://stackoverflow.com/q/40614995)? – dbc Oct 03 '17 at 09:42
  • Or, when checking `property.DeclaringType == typeof(Order)`, try getting the real, underlying type of the `DeclaringType` as shown in [unexpected GetType() result for entity entry](https://stackoverflow.com/q/16004707). – dbc Oct 03 '17 at 09:43
  • Indeed I am working with Entity Framework, sorry I didn't mention it. Disabling dynamic proxes solves the problem of the "self referencing loop", but not the problem of ignoring the desired properties in the nested classes through a ContractResolver. Which is the problem I'm currently trying to solve. – Kostas Dafnomilis Oct 03 '17 at 09:50
  • In that case can you provide a [mcve]? In this [fiddle](https://dotnetfiddle.net/4SVQXS) you can see that `CustomerContractResolver` is correctly suppressing the `Order.Customer` property while `DefaultContractResolver` is not. – dbc Oct 03 '17 at 09:55
  • 1
    It works! I needed to use the property.DeclaringType.BaseType instead of just property.DeclaringType. The CreateProperty method was being invoked for a set of Order types and also a set of dynamicProxies.Order types. Only the second where connected to the real entity but was setting the ShouldSerialize for the false ones. Thanks a lot dbc! – Kostas Dafnomilis Oct 03 '17 at 10:05

1 Answers1

0

From the exception error message it appears you are using Entity Framework and have enabled dynamic proxies:

When creating instances of POCO entity types, the Entity Framework often creates instances of a dynamically generated derived type that acts as a proxy for the entity.

Because the objects passed in are derived, the check property.DeclaringType == typeof(Order) will fail. Instead you can follow use one of the answers from unexpected GetType() result for entity entry to check whether the underlying type is Order, for instance:

public class CustomerContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        if (typeof(Order).IsAssignableFrom(property.DeclaringType) && property.PropertyName.Equals(nameof(Order.Customer)))
        {
            property.ShouldSerialize = instance => { return false; };
        }
        return property;
    }
}

Checking whether the property's declaring type is assignable from Order should work whether or not dynamic proxies are being used.

dbc
  • 104,963
  • 20
  • 228
  • 340