JavascriptSerializer serializing property twice wh

2019-02-27 16:25发布

Trying to create a service to return an object that has many shared properties, but one property should be highly limited in some situations. This is resulting in odd and undesired behaviour, where a property name is reused in the serialized output, resulting in incorrect behaviour in the browser. Here is an example that can be pasted into LINQPad (if you add a reference to System.Web.Extensions) to see the problem:

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"; } }
}

This prints:

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

When JSON.parse is called in the browser, the second Status overrides the first, meaning Status is always null.

The only way I can see of fixing this is to refactor so that FullStatus and LimitedStatus both inherit from a common parent and use override rather that new - bit more complicated in the real world code, but possible. Is my assumption correct? And also, I'd be interested to know if this is expected behaviour or a bug.

1条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-02-27 16:44

Yes, your assumption is correct. The new keyword is not the same as override; it merely "hides" the base class property, but the original property is still there and can still be discovered via reflection (which is how serializers do their work).

Generally, it is considered a "code smell" to define a method or property in a base class and then replace it with another method or property in a derived class that takes away functionality of the base class. This violates the Liskov Substitution Principle.

Instead of deriving Limited from Full, I would propose that you make an abstract base class for them and put the common stuff in there. Then, you can ADD things to each subclass which are different or exclusive to each (i.e. your differently-typed Status members).

For example:

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"; } }
}

Output:

{"Status":{"Text":"Loads and loads and loads of things"},"Stuff":"Common things"}
{"Status":{"Text":"A few things"},"Stuff":"Common things"}
查看更多
登录 后发表回答