开发者

Is there a way to make JavaScriptSerializer ignore properties of a certain generic type?

I am using the Entity Framework for my models, and i have need to serialize them to JSON. The problem is that EF includes all these really nice navigational collection开发者_开发技巧s (For instance my User model has an Orders property on it) and when I go to serialize these objects the serializer tries to get the value for those collections and EF yells at me for trying to use a disposed context

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

I know I can decorate my properties with [ScriptIgnore] to make the serializer leave them alone, but thats a problem with EF as it generates the code for those properties.

Is there a way to make the serializer not serialize properties that are of the generic type EntityCollection<>?

Alternatively is there a way to do this with another robust json library like JSON.Net?


You can declare a custom contract resolver which indicates which properties to ignore. Here's a general-purpose "ignorable", based on the answer I found here:

/// <summary>
/// Special JsonConvert resolver that allows you to ignore properties.  See https://stackoverflow.com/a/13588192/1037948
/// </summary>
public class IgnorableSerializerContractResolver : DefaultContractResolver {
    protected readonly Dictionary<Type, HashSet<string>> Ignores;

    public IgnorableSerializerContractResolver() {
        this.Ignores = new Dictionary<Type, HashSet<string>>();
    }

    /// <summary>
    /// Explicitly ignore the given property(s) for the given type
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName">one or more properties to ignore.  Leave empty to ignore the type entirely.</param>
    public void Ignore(Type type, params string[] propertyName) {
        // start bucket if DNE
        if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>();

        foreach (var prop in propertyName) {
            this.Ignores[type].Add(prop);
        }
    }

    /// <summary>
    /// Is the given property for the given type ignored?
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool IsIgnored(Type type, string propertyName) {
        if (!this.Ignores.ContainsKey(type)) return false;

        // if no properties provided, ignore the type entirely
        if (this.Ignores[type].Count == 0) return true;

        return this.Ignores[type].Contains(propertyName);
    }

    /// <summary>
    /// The decision logic goes here
    /// </summary>
    /// <param name="member"></param>
    /// <param name="memberSerialization"></param>
    /// <returns></returns>
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (this.IsIgnored(property.DeclaringType, property.PropertyName)) {
            property.ShouldSerialize = instance => { return false; };
        }

        return property;
    }
}

And usage:

var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };


If the idea is simply to return those objects to the client side why don't you just return what you need using anonymous classes?

Assuming you have this ugly heavy list of EntityFrameworkClass objects, you could do this:

var result = (from c in List<EntityFrameworkClass> 
             select new { 
                        PropertyINeedOne=c.EntityFrameworkClassProperty1,
                        PropertyINeedTwo=c.EntityFrameworkClassProperty2
              }).ToList();


This is my little contribution. Some changes to @drzaus answer. Description: Some resharped changes and Fluent enabled. And a little fix to use PropertyType instead of DeclaringType.

public class IgnorableSerializerContractResolver : DefaultContractResolver
{
    protected readonly Dictionary<Type, HashSet<string>> Ignores;

    public IgnorableSerializerContractResolver()
    {
        Ignores = new Dictionary<Type, HashSet<string>>();
    }

    /// <summary>
    /// Explicitly ignore the given property(s) for the given type
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName">one or more properties to ignore.  Leave empty to ignore the type entirely.</param>
    public IgnorableSerializerContractResolver Ignore(Type type, params string[] propertyName)
    {
        // start bucket if DNE
        if (!Ignores.ContainsKey(type))
            Ignores[type] = new HashSet<string>();

        foreach (var prop in propertyName)
        {
            Ignores[type].Add(prop);
        }

        return this;
    }

    /// <summary>
    /// Is the given property for the given type ignored?
    /// </summary>
    /// <param name="type"></param>
    /// <param name="propertyName"></param>
    /// <returns></returns>
    public bool IsIgnored(Type type, string propertyName)
    {
        if (!Ignores.ContainsKey(type)) return false;

        // if no properties provided, ignore the type entirely
        return Ignores[type].Count == 0 || Ignores[type].Contains(propertyName);
    }

    /// <summary>
    /// The decision logic goes here
    /// </summary>
    /// <param name="member"></param>
    /// <param name="memberSerialization"></param>
    /// <returns></returns>
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (IsIgnored(property.PropertyType, property.PropertyName))
        {
            property.ShouldSerialize = instance => false;
        }

        return property;
    }
}

usage:

// Ignore by type, regardless property name
var jsonResolver = new IgnorableSerializerContractResolver().Ignore(typeof(PropertyName))
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };


Adding on to @drzaus's answer, I modified the IsIgnored method to make it more generic -

public bool IsIgnored(Type type, string propertyName)
    {
        var ignoredType = this.Ignores.Keys.FirstOrDefault(t => type.IsSubclassOf(t) || type == t || t.IsAssignableFrom(type));
        //if (!this.Ignores.ContainsKey(type)) return false;

        if (ignoredType == null) return false;

        // if no properties provided, ignore the type entirely
        if (this.Ignores[ignoredType].Count == 0) return true;

        return this.Ignores[ignoredType].Contains(propertyName);
    }

And its usage:

var jsonResolver = new IgnorableSerializerContractResolver();
jsonResolver.Ignore(typeof(BaseClassOrInterface), "MyProperty");

The above helped me make a generic serializer for converting Thrift objects to standard JSON. I wanted to ignore the __isset property while serializing objects of classes that implement the Thrift.Protocol.TBase interface.

Hope it helps.

PS - I know converting a Thrift object to standard JSON defeats its purpose, but this was a requirement for interfacing with a legacy system.


If you use JSON.NET you can use attributes like JsonIgnore to ignore certain properties. I use this feature when serializing objects loaded via NHibernate.

I think there is a possibility to add conventions, too. Maybe you can implement a filter for your EF properties.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜