I have a json structure, that a few levels down turns a generic object
into one that would need to be converted into a Custom Object
type for further processing.
Cut down example to show what I mean:
{
"main": [
{
...
"list": [
{
...
"sublist": [
{
...
"items": [
{
<identical structure> <-- Section sets parameters, static strings
"Generic Object": {...} <-- Becomes ObjectType1 object, based on above
},
{
<identical structure> <-- Section sets parameters, static strings
"Generic Object": {...} <-- Becomes ObjectType2 object, based on above
}]
}]
}]
}]
}
I have the "Generic Object" set simply to type Object
in initial de-serialization. However, when I try to convert it to a more specialized type by
var object = (TypecastObjectType1)<locationOfObject>.VariableObject;
This results in an error at that specific line:
'Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'TypecastObjectType1'.'
Looking at source Json and the structure, it's matching my custom get-set class layout, so I am obviously missing something.
The reason it's a generic object itself, is because it is only typecast when that specific part is used/interacted with, which would then set the object type at that point for a more specialized selections of contents for that specific operation type.
The error message tells me that the <locationOfObject>.VariableObject
is obtained using LINQ to JSON, so its type is JObject
. To deserialize a JObject
into a .NET object, you should consider using <locationOfObject>.VariableObject.ToObject<TypecastObjectType1>()
instead of a simple cast.
To handle "Generic Objects", I'd suggest creating a custom serialization binder. Your JSON will look like this:
{
<identical structure>
"Generic Object": {
"$type": "TypecastObjectType1", // or "TypecastObjectType2"
<other properties>
}
}
The binder itself is a class derived from Newtonsoft.Json.Serialization.DefaultSerializationBinder
. In your case it will look like this:
using Newtonsoft.Json.Serialization;
public class GenericObjectBinder : DefaultSerializationBinder {
private static readonly Dictionary<string, Type> string2type = new() {
{ "1", typeof(TypecastObjectType1) }, // Map "$type" value to .NET type
{ "2", typeof(TypecastObjectType2) }
};
private static readonly Dictionary<Type, string> type2string = string2type.ToDictionary(pair => pair.Value, pair => pair.Key); // Map .NET type to "$type" value
public override Type BindToType(string? assemblyName, string typeName) {
if (string2type.TryGetValue(typeName, out var type) && assemblyName == null) {
return type;
}
return base.BindToType(assemblyName, typeName);
}
public override void BindToName(Type serializedType, out string? assemblyName, out string typeName) {
if (type2string.TryGetValue(serializedType, out var name)) {
assemblyName = null;
typeName = name;
return;
}
base.BindToName(serializedType, out assemblyName, out typeName!);
}
}
To inject this binder you'll need the following JsonSerializerSettings
:
var jsonSettings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto,
SerializationBinder = new GenericObjectBinder()
};