2012-12-16 57 views
2

在某些應用程序中,它允許Web端點的使用者在返回的對象圖中指定他們想要的屬性的哪個子集,並根據請求進行自定義。調用者會在查詢字符串/請求中指出他們想要的屬性。需要定製每個請求的序列化,而不是每個類型的結構。我不能想到解決這個問題的理想方法 - 理想情況下,它不強制對視圖模型(要進行序列化的對象)進行任何更改,而只是在每個端點啓用此功能(例如,通過在端點上放置屬性)。Json.net:條件屬性序列化 - 每個Web請求

今天有一種方法可以做到這一點,例如在MVC4/Web Api中,但它並不理想。 JSonPropery上的ShouldSerialize謂詞幾乎不需要更改任何視圖模型,除了回調僅提供實例/對象和沒有上下文(用戶需要的屬性,您在對象圖中的位置)之外。如果您只需過濾一種類型就可以了,那麼這可以工作(如果您使用request-global HttpContext),但不適用於更一般的解決方案進行分層過濾和/或類型可以出現在對象圖。

現在,我看到要做到這一點的一種方式是獲得序列化視圖模型的基類/接口,然後實現ISerializable。您還必須編寫自己的MediaTypeFormatter,以便您可以將所需內容填充到流式上下文中,以完成過濾所需的工作,如查詢字符串中指定的字段,以及訪問具有方便的Path屬性的寫入器可以用來輕鬆地對屬性名稱進行分層和/或通配符過濾。

我看不到沒有觸摸視圖模型對象的方式。我們需要在序列化程序中添加功能。一種方法是將流上下文添加到ShouldSerialize回調簽名(可能需要JSonProperty接口上的新方法,除非可以指定傳入的實例對象的格式也包含流上下文)。它仍然是我們的工作,添加按屬性名稱過濾的邏輯。另一種方法是將邏輯烘焙到JSonSerializerInternalWriter :: ShouldSerialize方法上,並在序列化設置中有條件地啓用該功能。

有關如何在不觸摸視圖模型的情況下執行此操作的其他想法?

回答

0

兩種可能的解決方案

  1. 我的想法會被使用IContractResolver描述here,但控制它的一些時尚c'tor參數。

    public class ShouldSerializeContractResolver:DefaultContractResolver { private private readonly bool _alwaysIncludeManager; public 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