Skip to content

Commit

Permalink
Merge pull request #587 from SteveDunn/565-support-for-trimming-no-re…
Browse files Browse the repository at this point in the history
…flection-in-systemtextjson

Support for trimmed/no-reflection/AOT with System.Text.Json
  • Loading branch information
SteveDunn authored May 8, 2024
2 parents 2fd5bed + de55e69 commit 8562c9e
Show file tree
Hide file tree
Showing 22,202 changed files with 22,576 additions and 22,219 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
6 changes: 6 additions & 0 deletions Consumers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplication", "samples\W
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApplicationConsumer", "samples\WebApplicationConsumer\WebApplicationConsumer.csproj", "{CCFC28B7-3E56-4364-AAE9-83EC01FBE820}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AotTrimmedSample", "samples\AotTrimmedSample\AotTrimmedSample.csproj", "{99CBD5AD-854A-4678-8667-2D1C4434D551}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -43,6 +45,10 @@ Global
{CCFC28B7-3E56-4364-AAE9-83EC01FBE820}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCFC28B7-3E56-4364-AAE9-83EC01FBE820}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCFC28B7-3E56-4364-AAE9-83EC01FBE820}.Release|Any CPU.Build.0 = Release|Any CPU
{99CBD5AD-854A-4678-8667-2D1C4434D551}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99CBD5AD-854A-4678-8667-2D1C4434D551}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99CBD5AD-854A-4678-8667-2D1C4434D551}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99CBD5AD-854A-4678-8667-2D1C4434D551}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
26 changes: 26 additions & 0 deletions samples/AotTrimmedSample/AotTrimmedSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseLocallyBuiltPackage>true</UseLocallyBuiltPackage>
<RootNamespace>MyApp</RootNamespace>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>

<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
<TrimMode>partial</TrimMode>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

<ItemGroup Condition=" '$(UseLocallyBuiltPackage)' != ''">
<PackageReference Include="Vogen" Version="999.9.*" />
</ItemGroup>

<ItemGroup Condition=" '$(UseLocallyBuiltPackage)' == ''">
<PackageReference Include="Vogen" Version="999.9.10219943" />
</ItemGroup>

</Project>
52 changes: 52 additions & 0 deletions samples/AotTrimmedSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Vogen;

// to *not* generate the factory, use:
// [assembly: VogenDefaults(systemTextJsonConverterFactoryGeneration: SystemTextJsonConverterFactoryGeneration.Omit)]

Person person = new()
{
Name = Name.From("Fred Flintstone"),
Age = Age.From(44),
Address = Address.From("201 Cobblestone Lane"),
};

var options = new JsonSerializerOptions
{
WriteIndented = true,
Converters =
{
new VogenTypesFactory()
}
};

var ctx = new JsonSourceGenerationContext(options);

var json = JsonSerializer.Serialize(person, ctx.Person);
Person person2 = JsonSerializer.Deserialize(json, ctx.Person)!;

Console.WriteLine(json);
Console.WriteLine($"{person2.Name} is {person2.Age}, and lives at {person2.Address}");

public class Person
{
public Age Age { get; set; }
public Name Name { get; set; }
public Address Address { get; set; }
}

[ValueObject<int>]
public partial struct Age {}

[ValueObject<string>]
public partial struct Name {}

[ValueObject<string>]
public partial struct Address {}

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Person))]
internal partial class JsonSourceGenerationContext : JsonSerializerContext
{
}
35 changes: 35 additions & 0 deletions samples/AotTrimmedSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
An example of using Vogen with System.Text.Json (STJ) source-generated serialization.

Reflection cannot be used in trimmed, and/or AOT compiled .NET applications.

Additionally, STJ converters, as produced by Vogen, aren't available
when STJ does its source generation.

The solution to this is for Vogen to source-generate a 'factory', which tells
STJ the respective converters for each value object generated.

A typical example is something like:

```c#
var options = new JsonSerializerOptions
{
WriteIndented = true,
Converters =
{
new VogenTypesFactory()
}
};

var ctx = new JsonSourceGenerationContext(options);

var json = JsonSerializer.Serialize(person, ctx.Person);
Person person2 = JsonSerializer.Deserialize(json, ctx.Person)!;
```

This sample produces a self-contained AOT binary for `win-x64`.

**Note**
If you _don't_ want the factory built, then this is configurable in the global config with:

`[assembly: VogenDefaults(
systemTextJsonConverterFactoryGeneration: SystemTextJsonConverterFactoryGeneration.Omit)]`
22 changes: 22 additions & 0 deletions src/Vogen.SharedTypes/SystemTextJsonConverterFactoryGeneration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Diagnostics;

namespace Vogen;

/// <summary>
/// Defines if a JSON converter 'factory' is generated containing value objects with System.Text.Json converters.
/// These factories can be used in various scenarios with System.Text.Json, including source-generation.
/// </summary>
public enum SystemTextJsonConverterFactoryGeneration
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Unspecified = -1,
/// <summary>
/// Do not generate the factory.
/// </summary>
Omit = 0,

/// <summary>
/// Generate the factory.
/// </summary>
Generate = 1
}
2 changes: 1 addition & 1 deletion src/Vogen.SharedTypes/ValueObjectAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Vogen
{
using System;

// Generic attributes were introduced in .NET 5 and C# 9
// Generic attributes were introduced in C# 11
/// <summary>
/// Marks a type as a Value Object. The type should be partial so that the
/// source generator can augment the type with equality and validation.
Expand Down
3 changes: 2 additions & 1 deletion src/Vogen.SharedTypes/VogenDefaultsAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ public VogenDefaultsAttribute(
ParsableForStrings parsableForStrings = ParsableForStrings.GenerateMethodsAndInterface,
ParsableForPrimitives parsableForPrimitives = ParsableForPrimitives.HoistMethodsAndInterfaces,
TryFromGeneration tryFromGeneration = TryFromGeneration.Unspecified,
IsInitializedMethodGeneration isInitializedMethodGeneration = IsInitializedMethodGeneration.Unspecified
IsInitializedMethodGeneration isInitializedMethodGeneration = IsInitializedMethodGeneration.Unspecified,
SystemTextJsonConverterFactoryGeneration systemTextJsonConverterFactoryGeneration = SystemTextJsonConverterFactoryGeneration.Unspecified
)
{
}
Expand Down
10 changes: 9 additions & 1 deletion src/Vogen/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,12 @@ VOG025 | Usage | Error | DoNotUseReflection

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
VOG026 | Usage | Warning | DoNotDeriveFromVogenAttributes
VOG026 | Usage | Warning | DoNotDeriveFromVogenAttributes

## Release 4.0.2

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
VOG027 | Usage | Error | DoNotUseNewAnalyzer
1 change: 0 additions & 1 deletion src/Vogen/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
VOG027 | Usage | Error | DoNotUseNewAnalyzer
14 changes: 11 additions & 3 deletions src/Vogen/BuildConfigurationFromAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ internal class BuildConfigurationFromAttributes
private ParsableForStrings _parsableForStrings;
private ParsableForPrimitives _parsableForPrimitives;
private TryFromGeneration _tryFromGeneration;
private IsInitializedMethodGeneration _isInitializedMethodGeneration;
private IsInitializedMethodGeneration _isInitializedMethodGeneration;
private SystemTextJsonConverterFactoryGeneration _systemTextJsonConverterFactoryGeneration;

private BuildConfigurationFromAttributes(AttributeData att)
{
Expand All @@ -51,6 +52,7 @@ private BuildConfigurationFromAttributes(AttributeData att)
_hasErroredAttributes = false;
_tryFromGeneration = TryFromGeneration.Unspecified;
_isInitializedMethodGeneration = IsInitializedMethodGeneration.Unspecified;
_systemTextJsonConverterFactoryGeneration = SystemTextJsonConverterFactoryGeneration.Unspecified;

_diagnostics = new List<Diagnostic>();

Expand Down Expand Up @@ -108,7 +110,8 @@ private VogenConfigurationBuildResult Build(bool argsAreFromVogenDefaultAttribut
_parsableForStrings,
_parsableForPrimitives,
_tryFromGeneration,
_isInitializedMethodGeneration),
_isInitializedMethodGeneration,
_systemTextJsonConverterFactoryGeneration),
diagnostics: _diagnostics);
}

Expand Down Expand Up @@ -180,7 +183,7 @@ private void PopulateDiagnosticsWithAnyValidationIssues()
// ReSharper disable once CognitiveComplexity
private void PopulateFromVogenDefaultsAttributeArgs(ImmutableArray<TypedConstant> argsExcludingUnderlyingType)
{
if (argsExcludingUnderlyingType.Length > 12)
if (argsExcludingUnderlyingType.Length > 13)
{
throw new InvalidOperationException("Too many arguments for the attribute.");
}
Expand All @@ -194,6 +197,11 @@ private void PopulateFromVogenDefaultsAttributeArgs(ImmutableArray<TypedConstant
continue;
}

if (i == 12)
{
_systemTextJsonConverterFactoryGeneration = (SystemTextJsonConverterFactoryGeneration) v;
}

if (i == 11)
{
_isInitializedMethodGeneration = (IsInitializedMethodGeneration) v;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Templates/Byte/Byte_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Templates/Char/Char_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#if NET7_0_OR_GREATER_DO_NOT_USE
class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand All @@ -12,7 +12,7 @@ public override void Write(System.Text.Json.Utf8JsonWriter writer, VOTYPE value,
}
}
#elif NET6_0_OR_GREATER
class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
 class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
 internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Templates/Guid/Guid_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Templates/Int/Int_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Templates/Long/Long_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Templates/Short/Short_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#if NET7_0_OR_GREATER_DO_NOT_USE // as of .NET 7, STJ doesn't seem to have a `GetTimeOnly` method
class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand All @@ -12,7 +12,7 @@ public override void Write(System.Text.Json.Utf8JsonWriter writer, VOTYPE value,
}
}
#elif NET6_0_OR_GREATER
class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
internal class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
Expand Down
Loading

0 comments on commit 8562c9e

Please sign in to comment.