I'm trying to find an answer to a problem that has stumped me for a few days, I'm converting some legacy WCF code over to SignalR, it's an internal API and the client is Silverlight for the moment.
I have a .NET class (Content1) on the server side that has a matching JSON converter and a message class (Message1) that has a property of type Content1. This all seems to work fine, the problem is when that property is changed to type object (which the current code is)
public class Message1
{
public string Name { get; set; }
public object Payload { get; set; }
}
Now my custom JsonConverter is no longer called.
I've placed the converter in the JsonSerializer.Converters collection and I can see the CanConvert method being hit on the converter, but the property comes through as a request to convert System.Object.
I've enabled typeNameHandling and set it to Auto (All/Object/Array are not an option as SignalR breaks), and can see the $type property being written into my JSON
{
"Name": "Test Message 1",
"Payload": {
"$type": "Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3",
"Propert1": "This is a string"
}
}
and with tracing turned on I can see the type being resolved
Resolved type 'Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3to Models.Content1. Path 'Payload.$type'.
But my custom converter is never called.
So the crux of my issue is, is there a way to get Json.Net to delegate to my class level custom converter when using $type?
Or failing that, if I write a custom converter and register it for my object property
public class Message1
{
public string Name { get; set; }
[JsonConverter(typeof(YetAnotherConverter))]
public object Payload { get; set; }
}
Is there a way to peek ahead at the type of the object via the $type property, I'd really like to do this
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var data = JObject.Load(reader);
var type = data.Property("$type");
if (type.Value.Value<string>().Contains("Content1"))
{
var obj = serializer.Deserialize<Content1>(reader);
return obj;
}
if (type.Value.Value<string>().Contains("Content2"))
{
var obj = serializer.Deserialize<Content2>(reader);
return obj;
}
return serializer.Deserialize(reader);
}
Which would invoke the correct JsonConverters for my type, but in reality does not work as JsonReader is forward only so I can't 'peek' at the type.
I suppose I could go the route of making the json serialise more like below
{
"Name": "Test Message 1",
"Payload": {
"$type": "Models.Content1, Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0d90b1aaa82178d3",
"data": {
"Propert1": "This is a string"
}
}
}
Then I can edge forward through the JSON with the reader, get the $type (should probably use a different name at this point as I've completely mangled in original use), find the data section and then pass that off to the serialiser with the correct object type so it would invoke the converter in the class level attribute.
But this honestly feels like I'm heading of down a rabbit hole so deep, it can't be right!
Thanks
Stephen.