Repository navigation
Description
Hello there!
Right now, JsonSubTypes does not (seem to) support serializing sub types that have generic type parameters, at least not in a generic way when using JsonSubtypesConverterBuilder to add a converter to the JsonSerializerSettings.
In my current project, I have a base type with many sub types and I register them using a converter builder. One of those sub types has generic type parameters, and it's not really an option to explicitly register all possible concrete types that might be used with it.
It would be cool if we could register a generic sub type without specifying type parameters, like .RegisterSubtype(typeof(Some<>), "Some"). At the very least, it would be nice if serializing objects of this type would work, and bonus points for also supporting deserialization (but this is probably hard as Json.NET would be responsible for this part).
Like said, I've considered registering the type with all possible concrete used types in the application, but this is brittle. I thought maybe I could scan the entire application for all uses of the generic type, but that's a lot of work.
I checked the code and saw that right now types for serialization are saved as dictionary keys. Naturally, the incoming type does not match since Some<int> is not equal to Some<T>. Maybe you could add a fallback in case serialization fails and the input has a generic type. Something like this (pseudo code):
if (!tryToSerializeWithType(input, inputType) && inputType.IsGenericType)
{
if (!tryToSerializeWithType(input, inputType.GetGenericTypeDefinition()))
{
throw new Exception("Impossible to serialize type");
}
}Full example of what I'm trying to achieve:
using System;
using JsonSubTypes;
using Newtonsoft.Json;
namespace subTypesIssue
{
class Program
{
static void Main()
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(JsonSubtypesConverterBuilder
.Of<IResult>("Type")
.RegisterSubtype(typeof(Some<>), "Some")
.RegisterSubtype(typeof(None), "None")
.SerializeDiscriminatorProperty()
.Build());
var input = new Some<int> {Value = 42};
var json = JsonConvert.SerializeObject(input, settings);
// Would expect {"Type":"Some","Value":42} but get "Impossible to serialize type" error
Console.WriteLine(json);
// Would be nice if deserialization also worked and the generic type could be inferred from the value
var result = JsonConvert.DeserializeObject<IResult>(json, settings);
Console.WriteLine(result);
}
}
public interface IResult
{
}
public class Some<T> : IResult
{
public T Value { get; set; }
}
public class None : IResult
{
}
}
Activity
manuc66 commentedon Sep 15, 2021
Hi,
Here is the not working solution https://dotnetfiddle.net/wBWRMw
The is no enough information in
.RegisterSubtype(typeof(Some<>), "Some")to know which target type need to be instantiated, how would you resole to the fact that it should deserialize toSome<int>?EdeMeijer commentedon Sep 17, 2021
Yeah, I figured it would probably be impossible to deserialize. But what about only serialization? In this particular instance, that's really the only thing I need.
That said, I'd understand if you would not want to add additional logic for a serialize-only solution.
EdeMeijer commentedon Sep 17, 2021
Actually, this just made me think. Why not also use extra JSON properties to encode the generic type(s)? The above case could be serialized to
{"Type": "Some", "T": "System.Int32", "Value": 42}by default, and you could introduce extra attributes to annotate the type parameters in order to customize the JSON property. Then this information could be used to determine the concrete type during deserialization.I can imagine that including raw system type names (and any actual type names for that matter) in JSON is not desirable (not portable, potential security risk). In the same fashion as the "known subtypes" approach, maybe the user could configure a custom mapping of supported generic types to type names to use in the serialized JSON. Something like this:
With the above configuration, the serialized JSON would look like
{"Type": "Some", "ResultType": "Integer", "Value": 42}.I understand this is quite the extension to your library and might be out of scope, but I think it could be a valuable addition for some users.
manuc66 commentedon Sep 29, 2021
Hi @EdeMeijer
The following sample does not work with the lines in comment, but it seems to be close to something that already exists:
See: https://dotnetfiddle.net/EGqf2C
I've not debug yet the reason why...
EdeMeijer commentedon Sep 30, 2021
That's... interesting. You're stacking two
[JsonConverter]attributes in the same type hierarchy. But even is this would work, it would require the user to still declare every type he might be using as a sub type. Not exactly what I had in mind.That said, for my current use case, I worked around the problem in a different way (using come custom wrapper type) so I'd say, don't put too much effort into this if you don't feel like this is very important to you.
manuc66 commentedon Oct 22, 2021
Some generic handling implemented in : #140
manuc66 commentedon May 9, 2022
Published with version 1.9.0