SteveDunn / Vogen

A semi-opinionated library which is a source generator and a code analyser. It Source generates Value Objects
Apache License 2.0
734 stars 41 forks source link

Doesn't work with `System.Text.Json` source generators #612

Closed aradalvand closed 1 month ago

aradalvand commented 1 month ago

Describe the bug

Apparently this was reported in #565 and subsequently fixed in #587; but I'm still encountering the exact same problem on Vogen version 4.0.8 and .NET 8.

Steps to reproduce

  1. dotnet new console
  2. Put this in your Program.cs:
    
    using System.Text.Json;
    using System.Text.Json.Serialization;
    using Vogen;

var foo = new Foo(Test.From("abc")); Console.WriteLine( JsonSerializer.Serialize( foo ) ); Console.WriteLine( JsonSerializer.Serialize( foo, JsonContext.Default.Options ) );

public record Foo( Test Test );

[ValueObject] public readonly partial struct Test;

[JsonSerializable(typeof(Foo))] [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.KebabCaseLower)] public partial class JsonContext : JsonSerializerContext;

3. `dotnet run` and look at the output:

{"Test":"abc"} {"test":{}}



### Expected behaviour

The second `JsonSerializer.Serialize` call that uses the source generator should work.
SteveDunn commented 1 month ago

Hi @aradalvand - thanks for the bug report. I think the issue is with the documentation (or lack of!). You need to specify the converters in your options. The converters (or rather, a factory for converters), are generated by Vogen and are named VogenTypesFactory.

This snippet works:

using System.Text.Json;
using System.Text.Json.Serialization;
using Vogen;

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower,
    Converters =
    {
        new VogenTypesFactory()
    }
};

var foo = new Foo(Test.From("abc"));

var ctx = new JsonContext(options);
Console.WriteLine(JsonSerializer.Serialize(foo, ctx.Foo));

public record Foo(Test TestValueObject);

[ValueObject<string>]
public readonly partial struct Test;

[JsonSerializable(typeof(Foo))]
public partial class JsonContext : JsonSerializerContext;

... which results in { "test-value-object": "abc" }



Please let me know if this doesn't fix the issue and I'll reopen and investigate.

Thanks again!
aradalvand commented 1 month ago

@SteveDunn But JSON source generators are supposed to be able to pick up converters (provided that the types in question are annotated with the JsonConverter attribute) automatically. So having to do it like this is a bummer.

Having investigated the problem a little more after creating this issue, I found out that this is due to the fact that the Vogen source generator would need to run before the JSON one, in order for this automatic pickup to happen — see this Stack Overflow answer.

The workaround for this would be putting the Vogen value objects in a separate project, and then referencing that project in the one that contains the JsonSerializerContext; this would've worked perfectly had it not been for #613. Please take a look at that issue.

SteveDunn commented 1 month ago

Ah, yes, separate projects is a good solution. As that answer says, source generators are completely independent of each other.

I should get a chance to look at the internal to public item tonight or over the weekend.

Thanks for the feedback!

aradalvand commented 1 month ago

@SteveDunn Thank you <3

SteveDunn commented 1 month ago

@SteveDunn Thank you <3

Also, if it unblocks you in the meantime, you could add an InternalsVisibleTo in the project with the value objects