当子类中使用“new”时,JavascriptSerializer序列化属性两次

尝试创建一个服务来返回一个具有许多共享属性的对象,但在某些情况下,一个属性应该受到高度限制。 这导致奇怪和不期望的行为,其中属性名称在序列化输出中重用,导致浏览器中的行为不正确。 下面是一个可以粘贴到LINQPad的示例(如果添加对System.Web.Extensions的引用)以查看问题:

 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时,第二个Status会覆盖第一个,这意味着Status始终为null。

我可以看到解决这个问题的唯一方法是重构,以便FullStatus和LimitedStatus都从公共父级inheritance并使用override而不是在现实代码中更新一点,但可能。 我的假设是否正确? 而且,我有兴趣知道这是预期的行为还是错误。

是的,你的假设是正确的。 new关键字与override ; 它只是“隐藏”基类属性,但原始属性仍然存在,仍然可以通过reflection发现(这是序列化程序如何工作)。

通常,在基类中定义方法或属性,然后将其替换为派生类中除去基类function的另一个方法或属性,将其视为“ 代码异味 ”。 这违反了Liskov替代原则 。

我建议您为它们创建一个抽象基类,并将常见的东西放在那里,而不是从Full派生限制。 然后,您可以将事物添加到每个子类中,每个子类对于每个子类都是不同的或独占的(即您的不同类型的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"}