Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify converter for JsonSerializerContext #141

Open
fleed opened this issue Jul 5, 2024 · 3 comments
Open

Specify converter for JsonSerializerContext #141

fleed opened this issue Jul 5, 2024 · 3 comments

Comments

@fleed
Copy link

fleed commented Jul 5, 2024

When using version 1.0.0-beta08 of the library in a .net 8 app, if you specify a System.Text.Json.Serialization.JsonSerializerContext, the StronglyTypedId will be serialized as an empty object.

Definition:

[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, Converters = [typeof(ProjectId.ProjectIdSystemTextJsonConverter)])]
[JsonSerializable(typeof(Project))]
internal partial class ProjectSourceContext : JsonSerializerContext
{
}

Ideal usage:

[Fact]
    public async Task TestProjectSerializationDefaultAsync()
    {
        var project = new Project
        {
            Id = new ProjectId(new Guid("00000000-0000-0000-0000-000000000001")),
            Name = "Project 1"
        };
        var serialized = JsonSerializer.Serialize(project, ProjectSourceContext.Default.Project);
        await Verify(serialized);
    }

Value of serialized string:

{"id":{},"name":"Project 1"}

I am currently using the following solution:

[Fact]
    public async Task TestProjectSerializationAsync()
    {
        var project = new Project
        {
            Id = new ProjectId(new Guid("00000000-0000-0000-0000-000000000001")),
            Name = "Project 1"
        };
        var options = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };
        options.Converters.Add(new ProjectId.ProjectIdSystemTextJsonConverter());
        var context = new ProjectSourceContext(options);
        var serialized = JsonSerializer.Serialize(project, context.Project);
        await Verify(serialized);
    }

In this case, you will get the expected JSON: {"id":"00000000-0000-0000-0000-000000000001","name":"Project 1"}. If you comment out the line options.Converters.Add(), you will still receive the incorrect serialized string with an empty object as the id.

This is the ideal solution I'm looking for:

[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, Converters = [typeof(ProjectId.ProjectIdSystemTextJsonConverter)])]
[JsonSerializable(typeof(Project))]
internal partial class ProjectSourceContext : JsonSerializerContext
{
}

but I'm receiving the following compilation error:

Error SYSLIB1220 : The 'JsonConverterAttribute' type 'TestApp.Model.ProjectId.ProjectIdSystemTextJsonConverter' specified on member 'TestApp.Model.ProjectSourceContext' is not a converter type or does not contain an accessible parameterless constructor.

Is there another way to specify the converter for the Default serializer context object, to avoid specifying the options everywhere?

@andrewlock
Copy link
Owner

andrewlock commented Dec 22, 2024

Hey, sorry for the delay, I managed to miss this... did you ever get to the bottom of the issue? I've just given it a try and I can see that it has the behaviour you describe, but I'm not sure why 🤔 It's as though the source generator just isn't paying any attention to the Converters attribute for some reason (annoyingly the docs on it are very light too).

EDIT: I can't reproduce the compiler error you're seeing at all though 🤔 Which .NET version are you using? Neither .NET 8 or .NET 9 give the compilation error for me.

I stand corrected, I can reproduce the error, and that's likely the source of the issue. I haven't figured out why yet though...

@andrewlock
Copy link
Owner

Ok, I've just figured it out.

Source generators can't see the output of other source generators. So the Json Source Generator can't see the the converter generated by StronglyTypedId, hence the slightly strange compilation error.

Unfortunately, currently, there's no workaround for this, other than having the IDs defined in a different project to the project in which you're creating your JsonSerializerContext I think 🙁 Yes, that kinda sucks, but it's a limitation in the compiler currently.

@andrewlock
Copy link
Owner

One possible workaround is the approach I use in the tests:

internal static SystemTextJsonSerializerContext Custom
=> new(new JsonSerializerOptions
{
Converters =
{
new GuidId1.GuidId1SystemTextJsonConverter(),
new ConvertersGuidId.ConvertersGuidIdSystemTextJsonConverter(),
new ConvertersGuidId2.ConvertersGuidId2SystemTextJsonConverter(),
new ConvertersIntId.ConvertersIntIdSystemTextJsonConverter(),
new ConvertersLongId.ConvertersLongIdSystemTextJsonConverter(),
new ConvertersStringId.ConvertersStringIdSystemTextJsonConverter(),
}
});

This doesn't help with the Default case, but it does mean you can do something like this:

[JsonSerializable(typeof(Project))]
internal partial class ProjectSourceContext : JsonSerializerContext
{
    internal static SystemTextJsonSerializerContext Custom
        => new(new JsonSerializerOptions
        {
            Converters =
            {
                new ProjectId.ProjectIdSystemTextJsonConverter(),
            }
        });
}

And then you can use it like this:

var serialized = JsonSerializer.Serialize(project, ProjectSourceContext.Custom.Project);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants