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.
Yes, your assumption is correct. The
new
keyword is not the same asoverride
; 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
fromFull
, 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-typedStatus
members).For example:
Output: