2

If you create a custom Exception that overrides the virtual property Message with something like this:

public class GrossException : Exception
{
    public GrossException() : base("Eww, gross") { }
}

public class BarfException : GrossException
{
    public override string Message { get; } = "BARF!!";
}

Then, when sending BarfException using JSON.NET through ASP.NET, the Message property will contain

"Eww, gross"

Instead of the overridden value. I believe this is related to the fact that Exception implements ISerializable, but since the Message property is virtual, should I not be allowed to override it like this and still have it work?

Is there a proper way to implement Exception and be able to override the Message property and still have it work?

1 Answer 1

4

You are correct that this is happening because Exception implements ISerializable and Json.NET supports this interface. Specifically, from the reference source, in the method Exception.GetObjectData(SerializationInfo info, StreamingContext context), the Exception type serializes the underlying field, not the property:

        info.AddValue("Message", _message, typeof(String));

Microsoft may have done this because the Message property has a default value if the underlying field is not set:

    public virtual String Message {
           get {  
            if (_message == null) {
                if (_className==null) {
                    _className = GetClassName();
                }
                return Environment.GetResourceString("Exception_WasThrown", _className);

            } else {
                return _message;
            }
        }
    }

By serializing the field, not the property, an exception with the default message will have its visible message automatically shown in the CurrentUICulture of the receiving system when deserialized.

Thus, if you want the value of the message property to appear in the JSON, instead of the underlying field, you're going to need to override GetObjectData(). And, since AddValue() throws an exception if you try to add a value with the same name as a pre-existing value, and SerializationInfo has no SetValue() method to replace a current value, you're going to need to do something with a bit of code smell:

public class GrossException : Exception
{
    public GrossException() : base("Eww, gross") { }

    protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}

public class BarfException : GrossException
{
    public BarfException() : base() { }

    protected BarfException(SerializationInfo info, StreamingContext context) : base(info, context) { }

    public override string Message { get { return "BARF!!"; } }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        var tempInfo = new SerializationInfo(GetType(), new FormatterConverter());
        base.GetObjectData(tempInfo, context);
        foreach (SerializationEntry entry in tempInfo)
        {
            if (entry.Name != "Message")
            {
                info.AddValue(entry.Name, entry.Value, entry.ObjectType);
            }
        }
        info.AddValue("Message", Message);
    }
}

As you can see, this solution violates the design of your inheritance hierarchy since the value of the underlying _message field is no longer the value required by the base class GrossException. But at least the JSON is pretty.

A better solution would be to modify the GrossException type to have a protected constructor in which the message can be specified:

public class GrossException : Exception
{
    public GrossException() : base("Eww, gross") { }

    protected GrossException(SerializationInfo info, StreamingContext context) : base(info, context) { }

    protected GrossException(string message) : base(message) { }
}

Or, if you just want to see the overridden message in the JSON for debugging purposes (say, because you're logging exceptions), you could just add it to the serialization stream like so:

public class BarfException : GrossException
{
    public BarfException() : base() { }

    protected BarfException(SerializationInfo info, StreamingContext context) : base(info, context) { }

    public override string Message { get { return "BARF!!"; } }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue("OverriddenMessage", Message);
    }
}

Either solution avoids the code smell.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.