2

在某些应用程序中,允许 Web 端点的使用者在返回的对象图中指定他们想要的属性子集,根据请求进行自定义,这很方便。调用者将在查询字符串/请求中指出他们想要的属性。需要自定义每个请求的序列化,而不是每个类型结构。我想不出一个理想的方法来解决这个问题——理想情况下,它不强制对视图模型(要序列化的对象)进行任何更改,而只是为每个端点启用此功能(例如,通过在端点上放置一个属性)。

今天有一种方法可以做到这一点,例如在 MVC4/Web Api 中,但它并不理想。JSonPropery 上的 ShouldSerialize 谓词几乎是不需要更改任何视图模型所需的,除了回调只提供实例/对象而没有上下文(用户想要哪些属性,您在对象图中的位置)。如果您只对一种类型进行过滤就可以了,那么这将起作用(如果您使用请求全局 HttpContext),但这不适用于更通用的解决方案来进行分层过滤和/或当一个类型可以出现在多个级别中时对象图。

目前,我看到的一种方法是为要序列化的视图模型提供一个基类/接口,然后实现 ISerializable。您还必须编写自己的 MediaTypeFormatter 以便您可以将内容填充到流上下文中以完成过滤所需的工作,例如查询字符串中指定的字段以及访问具有方便 Path 属性的编写器可用于轻松地对属性名称进行分层和/或通配符过滤。

我看不到无需触摸视图模型对象的方法。我们需要在序列化程序中添加功能。一种方法是将流式上下文添加到 ShouldSerialize 回调签名中(可能需要在 JSonProperty 接口上使用新方法,除非可以选择指定传入的实例对象的格式也包含流式上下文)。添加按属性名称过滤的逻辑仍然是我们的工作。另一种方法是将逻辑烘焙到 JSonSerializerInternalWriter::ShouldSerialize 方法上,并在序列化设置中有条件地启用该功能。

关于如何在不接触视图模型的情况下执行此操作的任何其他想法?

4

1 回答 1

0

两种可能的解决方案

  1. 我的想法是使用此处描述的IContractResolver ,但使用 c'tor 参数以某种方式控制它。

    公共类 ShouldSerializeContractResolver : DefaultContractResolver { private readonly bool _alwaysIncludeManager; public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    public ShouldSerializeContractResolver(bool alwaysIncludeManager = true)
    {
        _alwaysIncludeManager = alwaysIncludeManager;
    }
    
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
    
        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e || _alwaysIncludeManager;
                };
        }
    
        return property;
    }
    

    }

  2. 对于更简单的事情,我过去只是在序列化之前将其转换为匿名对象:

-

List<Employee> employeeList = LoadEmployeeList();
bool alwaysIncludeManager = true; //Set by context
var outputList = employeeList.Select(x =>
    new
    {
        x.Name,
        Manager = (Manager != x || alwaysIncludeManager)
                    ? x.Manager : null
    });
//Then return this anonymous object, or serialize it
于 2014-09-09T15:39:27.840 回答