有没有办法让JavaScriptSerializer忽略某种泛型类型的属性?

时间:2011-07-27 22:13:37

标签: c# entity-framework json

我正在为我的模型使用Entity Framework,我需要将它们序列化为JSON。问题是EF包含了所有这些非常好的导航集合(例如我的用户模型上有一个Orders属性),当我去序列化这些对象时,序列化程序试图获取这些集合的值,EF对我大喊大叫使用已处置的上下文

  

ObjectContext实例已被释放,不能再用于需要连接的操作。

我知道我可以使用[ScriptIgnore]来装饰我的属性,以使序列化器不再使用它们,但这就是EF的问题,因为它会为这些属性生成代码。

有没有办法让序列化程序不序列化通用类型EntityCollection<>的属性?

或者有一种方法可以使用另一个强大的json库,如JSON.Net吗?

5 个答案:

答案 0 :(得分:8)

您可以声明自定义合约解析程序,指明要忽略的属性。这是一个基于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;
    }
}

用法:

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

答案 1 :(得分:3)

如果想法只是将这些对象返回给客户端,为什么不使用匿名类返回所需内容?

假设你有这个丑陋的EntityFrameworkClass对象列表,你可以这样做:

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

答案 2 :(得分:0)

这是我的小贡献。对@drzaus的一些更改回答。 说明:启用了一些重新更改和Fluent。还有一点修复使用PropertyType而不是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;
    }
}

用法:

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

答案 3 :(得分:0)

@drzaus's answer的基础上,我修改了IsIgnored方法以使其更通用-

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

及其用法:

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

以上内容帮助我制作了一个通用的序列化程序,用于将Thrift对象转换为标准JSON。在序列化实现__isset接口的类的对象时,我想忽略Thrift.Protocol.TBase属性。

希望有帮助。

PS-我知道将Thrift对象转换为标准JSON不能达到其目的,但这是与旧版系统接口的要求。

答案 4 :(得分:-2)

如果使用JSON.NET,则可以使用JsonIgnore等属性来忽略某些属性。在序列化通过NHibernate加载的对象时,我使用此功能。

我认为也有可能添加约定。也许你可以为你的EF属性实现一个过滤器。