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

时间:2014-10-06 11:46:13

标签: c# json serialization 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时,第二个状态会覆盖第一个状态,这意味着状态始终为空。

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

1 个答案:

答案 0 :(得分:1)

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

通常,它被视为" code smell"在基类中定义方法或属性,然后将其替换为派生类中的另一个方法或属性,它取消了基类的功能。这违反了Liskov Substitution Principle

我建议您为它们创建一个抽象基类,并将常用内容放在那里,而不是从Limited派生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"}