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

发布于 2024-11-26 19:48:14 字数 404 浏览 1 评论 0原文

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

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

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

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

或者有没有办法使用另一个强大的 json 库(如 JSON.Net)来做到这一点?

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 collections (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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

一笔一画续写前缘 2024-12-03 19:48:14

您可以声明一个自定义合约解析器来指示要忽略哪些属性。这是一个通用的“可忽略”,基于 我在这里找到的答案

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

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 };
走过海棠暮 2024-12-03 19:48:14

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

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

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

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();
﹏半生如梦愿梦如真 2024-12-03 19:48:14

这就是我的一点小小的贡献。对 @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 };

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 };
恍梦境° 2024-12-03 19:48:14

添加到 @drzaus 的答案,我修改了 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。我想在序列化实现 Thrift.Protocol.TBase 接口的类的对象时忽略 __isset 属性。

希望有帮助。

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

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.

瑾兮 2024-12-03 19:48:14

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文