17

I want to JSON serialize a custom exception object which inherits System.Exception. JsonConvert.SerializeObject seems to ignore properties from the derived type. The problem can be illustrated very simply:

class MyException : Exception {
    public string MyProperty { get; set; }
}

class Program {
    static void Main(string[] args) {
        Console.WriteLine(JsonConvert.SerializeObject(new MyException {MyProperty = "foobar"}, Formatting.Indented));
        //MyProperty is absent from the output. Why?
        Console.ReadLine();
    }
}

I've tried adding the DataContract and DataMember attributes in the correct places. They don't help. How do I get this to work?

wwarby
  • 1,873
  • 1
  • 21
  • 37
  • 1
    `Exception` is `ISerializable`. Try to override that. – Lucas Trzesniewski Nov 28 '14 at 23:50
  • I found the same solution as Thomas. Extending the ISerializable implementation is better than ignoring it. Consider, ISerializable was implemented by the creator of the base class for a reason. Ignoring it bypasses all of their work. When I used IgnoreSerializableInterface with my exception, the ClassName property was not included. As a result, I was unable to de-serialize it later. Extending GetObjectData and the constructor works perfectly. (I would have left this as a comment, but I have no reputation yet.) – Jack Whipnert Feb 08 '17 at 15:58

2 Answers2

19

Because Exception implements ISerializable, Json.Net uses that to serialize the object by default. You can tell it to ignore ISerializable like so:

var settings = new JsonSerializerSettings() {
    Formatting = Formatting.Indented,
    ContractResolver = new DefaultContractResolver() { 
        IgnoreSerializableInterface = true 
    } 
};
Console.WriteLine(JsonConvert.SerializeObject(new MyException {MyProperty = "foobar"}, settings));
Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • 4
    This will cause errors because exceptions have self-referencing members. You have to specify `ReferenceLoopHandling = ReferenceLoopHandling.Ignore` in the `JsonSerializerSettings` in addition to your code. Anyway I think serializing an exception this way is a really bad idea as the `TargetSite` property can generate a REALLY heavy string (like several MBytes). – Maxime Rossini Aug 29 '16 at 16:39
  • @MaximeRossini, you are right. But it's possible to use [`DynamicContractResolver`](http://stackoverflow.com/a/25158578/1046374) for ignoring `TargetSite` property. – Ivan Kochurkin Sep 27 '16 at 13:23
  • 1
    @MaximeRossini FYI, `TargetSite` is no longer included when serializing as per [this PR](https://github.com/JamesNK/Newtonsoft.Json/pull/1897) – pushkin Feb 04 '20 at 18:43
8

You could also add and retrieve a specific object into the System.Runtime.Serialization.SerializationInfo store by overriding the GetObjectData method and the ctor(SerializationInfo, StreamingContext):

public class MyCustomException : Exception
{
    public string MyCustomData { get; set; }

    protected MyCustomException (SerializationInfo info, StreamingContext context) : base(info, context)
    {
        MyCustomData = info.GetString("MyCustomData");
    }

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

This way the MyCustomObject property will get included in serialization and deserialization.

Allon Guralnek
  • 15,813
  • 6
  • 60
  • 93
Thomas C. G. de Vilhena
  • 13,819
  • 3
  • 50
  • 44
  • 3
    In addition to this, I needed to add the `[Serializable]` attribute to the derived exception class for it to work – dasch88 Jun 06 '18 at 15:33