Json.net:条件属性序列化 - 每个Web请求

时间:2012-12-16 02:53:49

标签: json serialization

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

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

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

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

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

1 个答案:

答案 0 :(得分:0)

两种可能的解决方案

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

    public class 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. 对于我过去更简单的事情,只是在序列化之前将其转换为匿名对象:

  3. -

    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