C
C#2mo ago
kalten

Support generic method NativeAOT JsonTypedInfo all together

Hello, I'm in the process of rewriting some of my libs to support native aot. But I have one use case for which I don't know how to do it. My use case look like this:
public interface IMyService
{
T Get<T>();
}
public class MyService(bool useV1) : IMyService
{
public T Get<T>()
{
return useV1
? GetA<T>()
: GetB<T>();
}

private T GetA<T>()
{
string json = "{\"Data\": {\"Name\":\"Hello\", \"Id\":1 }}";
return JsonSerializer.Deserialize<ModelA<T>>(json).Data;
}

private T GetB<T>()
{
string json = "{\"Data\": {\"Name\":\"World\", \"Id\":2 }}";
return JsonSerializer.Deserialize<ModelB<T>>(json).Data;
}
}

public class ModelA<T>
{
public T Data { get; set; }
}
public class ModelB<T>
{
public T Data { get; set; }
}
public interface IMyService
{
T Get<T>();
}
public class MyService(bool useV1) : IMyService
{
public T Get<T>()
{
return useV1
? GetA<T>()
: GetB<T>();
}

private T GetA<T>()
{
string json = "{\"Data\": {\"Name\":\"Hello\", \"Id\":1 }}";
return JsonSerializer.Deserialize<ModelA<T>>(json).Data;
}

private T GetB<T>()
{
string json = "{\"Data\": {\"Name\":\"World\", \"Id\":2 }}";
return JsonSerializer.Deserialize<ModelB<T>>(json).Data;
}
}

public class ModelA<T>
{
public T Data { get; set; }
}
public class ModelB<T>
{
public T Data { get; set; }
}
The issue is that JsonSerializer.Deserialize need either a JsonTypeInfo or a JsonSerializerOptions with TypeInfoResolver property set, to be working in NativeAot mode. But I don't know how to change IMyService without having to ask the user to provide two JsonTypeInfo parameters (One for ModelA<CustomData> and one for ModelB<CustiomData>) And using a JsonSerializerOptions would make the api less intuitive. Is there a way to JsonSourceGen only ModelA<> and ModelB<> in the lib and le the user provide the the T part? Any advice is welcome.
1 Reply
kalten
kalten2mo ago
I found a solution by using a temporary JsonElement type
private T GetB<T>(JsonTypeInfo<T> jTypeInfo)
{
string json = "{\"Data\": {\"Name\":\"World\", \"Id\":2 }}";
JsonElement r = JsonSerializer.Deserialize(json, LibJsonContext.Default.ModelBT)!.Data;
return SerializeUserModel<T>(r, jTypeInfo)!;
}

private T SerializeUserModel<T>(JsonElement jElement, JsonTypeInfo<T> jTypeInfo)
{
return jElement.Deserialize<T>(jTypeInfo)!;
}
private T GetB<T>(JsonTypeInfo<T> jTypeInfo)
{
string json = "{\"Data\": {\"Name\":\"World\", \"Id\":2 }}";
JsonElement r = JsonSerializer.Deserialize(json, LibJsonContext.Default.ModelBT)!.Data;
return SerializeUserModel<T>(r, jTypeInfo)!;
}

private T SerializeUserModel<T>(JsonElement jElement, JsonTypeInfo<T> jTypeInfo)
{
return jElement.Deserialize<T>(jTypeInfo)!;
}
But not sure about the efficiency of it Using a Utf8JsonReader seem to work too
private T GetAA<T>(JsonTypeInfo<T> jTypeInfo)
{
string json = "{\"Data\": {\"Name\":\"World\", \"Id\":2 }}";
Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json));
reader.Read();
reader.Read();

var propertyName = reader.GetString();
if (propertyName != nameof(ModelA.Data))
throw new Exception("Invalid property name");

reader.Read();

var data = JsonSerializer.Deserialize<T>(ref reader, jTypeInfo);

return data;
}
private T GetAA<T>(JsonTypeInfo<T> jTypeInfo)
{
string json = "{\"Data\": {\"Name\":\"World\", \"Id\":2 }}";
Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json));
reader.Read();
reader.Read();

var propertyName = reader.GetString();
if (propertyName != nameof(ModelA.Data))
throw new Exception("Invalid property name");

reader.Read();

var data = JsonSerializer.Deserialize<T>(ref reader, jTypeInfo);

return data;
}
But Utf8JsonReader is not usable in an async context
Want results from more Discord servers?
Add your server