I'm working on a protocol in which the receiver will receive json messages of certain specified custom types (currently 5, but could be 10-20). I'm struggling to come up with an optimal/fast solution which will automatically deserialize the json and return the correct type of object.
Example:
public class MessageA
{
public string Message;
}
public class MessageB
{
public int value;
}
public class MessageC
{
public string ValueA;
public string ValueB;
}
Ideally, the method should be like
Object Deserialize(string json);
and it will return one of the three message types OR null - in case there was a parsing error/the json didn't match any of the predefined type.
UPDATE: I have control over sender/receiver as well as the protocol design.
It would be helpful if the message could specify its type. Otherwise you have to infer it from some property or another.
You could use a message wrapper class when serializing, like this:
public class MessageWrapper<T>
{
public string MessageType { get { return typeof(T).FullName; } }
public T Message { get; set; }
}
So if you have a class Name
with a First
and Last
property, you could serialize it like this:
var nameMessage = new MessageWrapper<Name>();
nameMessage.Message = new Name {First="Bob", Last = "Smith"};
var serialized = JsonConvert.SerializeObject(nameMessage);
The serialized JSON is
{"MessageType":"YourAssembly.Name","Message":{"First":"Bob","Last":"Smith"}}
When deserializing, first deserialize the JSON as this type:
public class MessageWrapper
{
public string MessageType { get; set; }
public object Message { get; set; }
}
var deserialized = JsonConvert.DeserializeObject<MessageWrapper>(serialized);
Extract the message type from the MessageType
property.
var messageType = Type.GetType(deserialized.MessageType);
Now that you know the type, you can deserialize the Message
property.
var message = JsonConvert.DeserializeObject(
Convert.ToString(deserialized.Message), messageType);
message
is an object
, but you can cast it as Name
or whatever class it actually is.
var log = JsonConvert.DeserializeObject<Log>(File.ReadAllText("log.example.json");
public class Log
{
[JsonConverter(typeof(MessageConverter))]
public object[] Messages { get; set; }
}
public class MessageA
{
public string Message;
}
public class MessageB
{
public int value;
}
public class MessageC
{
public string ValueA;
public string ValueB;
}
public class MessageConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object ReadMessage(JObject jobject)
{
if (jobject.Property("Message") != null)
return jobject.ToObject<MessageA>(serializer);
if (jobject.Property("value") != null)
return jobject.ToObject<MessageB>(serializer);
if (jobject.Property("ValueA") != null)
return jobject.ToObject<MessageC>(serializer);
throw new Exception("Type is not recognized");
}
var jarray = JArray.Load(reader);
return jarray.Select(jitem => ReadMessage((JObject)jitem)).ToArray();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Json example:
{
"Messages":
[
{"Message": "System halted"},
{"value": 42},
{"ValueA": "Bob", "ValueB": "Smith"}
]
}
Hopefully you are familiar with the factory pattern, you could use factory(s) and include a "Type" property as part of the json, let's call it _t
.
You can either parse the json string yourself and find the _t
property's value, deserialise it to a dynamic
and get jsonObj._t
or have a simple class
with only a _t
field solely to deserialise the json into initially.
Then you can pass this string
representing the C# Type to the factory and get a json deserialiser for that Type
.
You can then make all of your outgoing and incoming calls add and process the _t
parameter respectively, so that new types are easy to add in future, by just adding and registering the serialisers/deserialisers you need for that Type
with the factory(s).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With