Reputation: 10045
JSON.NET (Newtonsoft.Json) can store the type of the serialized object in the output string if it is instructed to do so. By setting the TypeNameHandling to some of the values in the enum, dofferent from TypeNamehandling.None the output may look like this:
JsonConvert.SerializeObject(<object>,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All })
where is the object to be serialized.
As a result the output string will contain
{"$type":"<type_name>"...
On the other hand, the documentation states that the setting
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
will check type name during the deserialization ("Gets or sets how a type name assembly is written and resolved by the serializer." -- a copy of the JSON.NET documentation).
Despite I set an error handler explicitly no error is generated and therefore the handler remains silent. The deserialized object is not null, but all the properties which do not have corresponding names (and no conversion to their types from the serialized values exists) in the serialized content are nulls. This might be a desired behaviour, but what I would like to do is any sign of type mismatch between the serialized type and the type requested to be deserialized.
I this respect, what I wouldn't want to do is parse the serialized content, not that this is a big deal, but because it might be considered and internal info, pertinent to the serialization process.
And now my question: how can I get notified that the type of the serialized object is different from the type of the object requested to be deserialized?
PS: I use
JsonConvert.SerializeObject<T>(Object value, JsonSerializerSettings settings)
and
JsonConvert.DeserializeObject<T>(string value, JsonSerializerSettings settings)
Thanks!
Upvotes: 0
Views: 2720
Reputation: 116531
When resolving a type name, by default Json.NET uses DefaultSerializationBinder.BindToType(assemblyName, typeName)
, which eventually calls Assembly.LoadWithPartialName()
on most platforms:
#if !(NETFX_CORE || PORTABLE40 || PORTABLE)
// look, I don't like using obsolete methods as much as you do but this is the only way
// Assembly.Load won't check the GAC for a partial name
#pragma warning disable 618,612
assembly = Assembly.LoadWithPartialName(assemblyName);
#pragma warning restore 618,612
#elif NETFX_CORE || PORTABLE
assembly = Assembly.Load(new AssemblyName(assemblyName));
#else
assembly = Assembly.Load(assemblyName);
#endif
After some experimentation in Visual Studio, it appears that this method will silently and successfully match an assembly with different version numbers as long as the names and PublicKeyToken
values match, when loading DLLs from disk.
I'm not sure why this method is used, however you can replace the default binder with a subclass that checks that the expected and resolved assembly versions match perfectly, and if not, throws an exception (or does anything else you want):
public class AssemblyVersionSerializationBinder : DefaultSerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
var type = base.BindToType(assemblyName, typeName);
if (type != null)
{
var name = new AssemblyName(assemblyName);
// If assemblyName has a version specified and it does not match the version of the loaded assembly, raise an error.
if (name.Version != null && name.Version != type.Assembly.GetName().Version)
{
HandleAssemblyNameMismatch(type, assemblyName, typeName);
}
}
return type;
}
private void HandleAssemblyNameMismatch(Type type, string assemblyName, string typeName)
{
string message = string.Format("Mismatch between expected assembly name \"{0}\" and loaded assembly name \"{1}\"", type.Assembly.FullName, assemblyName);
Debug.WriteLine(message);
throw new JsonSerializationException(message);
}
}
And then use it like:
var settings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,
Binder = new AssemblyVersionSerializationBinder() };
Upvotes: 0
Reputation: 760
You can check the your resulting JSON
IDictionary<string, JToken> dictionary = JObject.Parse(json);
if (dictionary.ContainsKey("$type"))
Upvotes: 1