2014-10-06 44 views
1

試圖創建一個服務來返回具有許多共享屬性的對象,但在某些情況下,一個屬性應該受到高度限制。這導致了奇怪的和不希望的行爲,其中在序列化輸出中重用了屬性名稱,導致瀏覽器中的行爲不正確。這是看問題,可以(如果你添加到System.Web.Extensions參考)粘貼到LINQPad一個例子:在子類中使用「新」時,JavascriptSerializer會序列化屬性兩次

void Main() 
{ 
    System.Web.Script.Serialization.JavaScriptSerializer json = new System.Web.Script.Serialization.JavaScriptSerializer(); 

    json.Serialize(new Full(true)).Dump(); 
    json.Serialize(new Limited()).Dump(); 
} 

public class Full 
{ 
    public String Stuff { get { return "Common things"; } } 
    public FullStatus Status { get; set; } 

    public Full(bool includestatus) 
    { 
     if(includestatus) 
      Status = new FullStatus(); 
    } 
} 

public class Limited : Full 
{ 
    public new LimitedStatus Status { get; set; } 

    public Limited() : base(false) 
    { 
     Status = new LimitedStatus(); 
    } 
} 

public class FullStatus 
{ 
    public String Text { get { return "Loads and loads and loads of things"; } } 
} 

public class LimitedStatus 
{ 
    public String Text { get { return "A few things"; } } 
} 

此打印:

{"Stuff":"Common things","Status":{"Text":"Loads and loads and loads of things"}} 
{"Status":{"Text":"A few things"},"Stuff":"Common things","Status":null} 

當JSON.parse是所謂的瀏覽器,第二個狀態覆蓋第一個狀態,意味着狀態始終爲空。

我可以看到解決這個唯一的辦法就是重構,使FullStatus和LimitedStatus都來自一個共同的父繼承和使用override寧可new - 比較複雜一點在現實世界中的代碼,但可能。我的假設是否正確?而且,我很想知道這是否是預期的行爲或錯誤。

回答

1

是的,你的假設是正確的。 new關鍵字與override不相同;它只是「隱藏」基類屬性,但原始屬性仍然存在,仍然可以通過反射來發現(串行器是如何工作的)。

通常,它被認爲是「code smell」,用於在基類中定義方法或屬性,然後將其替換爲派生類中的另一個方法或屬性,該派生類帶走基類的功能。這違反了Liskov Substitution Principle

而不是從Full推導Limited,我會建議你爲它們製作一個抽象基類,並將普通的東西放在那裏。然後,您可以爲每個子類添加不同或排他的東西(即您的不同類型Status成員)。

例如:

class Program 
{ 
    static void Main(string[] args) 
    { 
     System.Web.Script.Serialization.JavaScriptSerializer json = 
      new System.Web.Script.Serialization.JavaScriptSerializer(); 

     Console.WriteLine(json.Serialize(new Full(true))); 
     Console.WriteLine(json.Serialize(new Limited())); 
    } 
} 

public abstract class Base 
{ 
    public String Stuff { get { return "Common things"; } } 
} 

public class Full : Base 
{ 
    public FullStatus Status { get; set; } 

    public Full(bool includestatus) 
    { 
     if (includestatus) 
      Status = new FullStatus(); 
    } 
} 

public class Limited : Base 
{ 
    public LimitedStatus Status { get; set; } 

    public Limited() 
    { 
     Status = new LimitedStatus(); 
    } 
} 

public class FullStatus 
{ 
    public String Text { get { return "Loads and loads and loads of things"; } } 
} 

public class LimitedStatus 
{ 
    public String Text { get { return "A few things"; } } 
} 

輸出:

{"Status":{"Text":"Loads and loads and loads of things"},"Stuff":"Common things"} 
{"Status":{"Text":"A few things"},"Stuff":"Common things"} 
+0

感謝您的聯繫!我標記你的回答是正確的,因爲它是我下降的路線 - 正確地試圖迅速破解某些東西。我仍然對序列化行爲背後的原因感興趣,但我猜JSON是生成的是完全有效的(至少在Chrome中 - 早先的屬性被默默地忽略),這不是序列化程序中的錯誤,只是一些讓程序員在他們的模型中修復。 – Whelkaholism 2014-10-14 09:34:01

+0

你的意思是爲什麼序列化程序在同一個對象中輸出兩個具有相同名稱的屬性?通常情況下,它不會,但是當你使用「new」關鍵字來隱藏一個屬性時,序列化程序會在它反映對象時發現兩個具有相同名稱的屬性,並且我猜測它在寫入時不會檢查這種情況JSON。在JSON的同一對象中擁有兩個具有相同名稱的屬性在技術上並不是每個[規範](http://tools.ietf.org/html/rfc7159)都是無效的,但它確實使JSON更少的可互操作性。 (續) – 2014-10-14 14:57:43

+0

諷刺的是,包括Json.Net在內的一些解析器在反序列化過程中無法處理包含重複屬性名稱的對象。出於這個原因,創建帶有重複屬性名稱的JSON被認爲是不好的形式,並且如果可能的話最好避免。 – 2014-10-14 15:01:40

相關問題