2014-10-06 21 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

相关问题