Skip to content

Commit

Permalink
Merge pull request #628 from SteveDunn/bug-stj-allows-nulls
Browse files Browse the repository at this point in the history
Bug stj allows nulls
  • Loading branch information
SteveDunn authored Jun 20, 2024
2 parents d6e3a72 + 0c3dd07 commit 3bfd05c
Show file tree
Hide file tree
Showing 1,996 changed files with 7,827 additions and 5 deletions.
8 changes: 7 additions & 1 deletion src/Vogen.SharedTypes/DeserializationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,18 @@ public enum DeserializationStrictness
/// </summary>
RunMyValidationMethod = 1 << 1,

/// <summary>
/// System.Text.Json only for now. By default, System.Text.Json allows nulls for reference types.
/// If you want to disallow this behaviour for value objects, then set this flag.
/// </summary>
DisallowNulls = 1 << 2,

/// <summary>
/// If your Value Object has 'Instances', then those are considered valid during deserialization.
/// If the incoming value doesn't match any known instance, and if your Value Object has a Validate method, it will
/// be called to validate the incoming value during deserialization.
/// </summary>
AllowValidAndKnownInstances = AllowKnownInstances | RunMyValidationMethod,

Default = AllowValidAndKnownInstances
Default = AllowValidAndKnownInstances,
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Vogen.Generators.Conversions;

Expand All @@ -22,19 +23,35 @@ public string GenerateAnyBody(TypeDeclarationSyntax tds, VoWorkItem item)
}

string code = ResolveTemplate(item);

bool allowNullReferences = true;

if (item.IsTheWrapperAReferenceType)
{
// it's a class
if (item.Config.DeserializationStrictness.HasFlag(DeserializationStrictness.DisallowNulls))
{
allowNullReferences = false;
}

}

code = allowNullReferences ? CodeSections.CutSection(code, "__HANDLE_NULL__") : CodeSections.KeepSection(code, "__HANDLE_NULL__");

if (code.Contains("__NORMAL__"))
{
(string keep, string cut) keepCut =
item.Config.Customizations.HasFlag(Customizations.TreatNumberAsStringInSystemTextJson)
? ("__STRING__", "__NORMAL__") : ("__NORMAL__", "__STRING__");
? ("__STRING__", "__NORMAL__")
: ("__NORMAL__", "__STRING__");

code = CodeSections.CutSection(code, keepCut.cut);
code = CodeSections.KeepSection(code, keepCut.keep);
}

code = code.Replace("VOTYPE", item.VoTypeName);
code = code.Replace("VOUNDERLYINGTYPE", item.UnderlyingTypeFullName);

return code;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
var primitive = global::System.Text.Json.JsonSerializer.Deserialize<VOUNDERLYINGTYPE>(ref reader, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetBoolean());
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Byte/Byte_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Char/Char_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
var s = reader.GetString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetDateOnly());
Expand All @@ -20,6 +23,9 @@ public override void Write(System.Text.Json.Utf8JsonWriter writer, VOTYPE value,
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return new VOTYPE(global::System.DateOnly.ParseExact(reader.GetString(), "yyyy-MM-dd", global::System.Globalization.CultureInfo.InvariantCulture));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetDateTime().ToUniversalTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetDateTimeOffset());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Double/Double_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Guid/Guid_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetGuid());
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Int/Int_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Long/Long_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Short/Short_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/Single/Single_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
#if NET5_0_OR_GREATER
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Templates/String/String_SystemTextJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return VOTYPE.__Deserialize(reader.GetTimeOnly());
Expand All @@ -20,6 +23,9 @@ public override void Write(System.Text.Json.Utf8JsonWriter writer, VOTYPE value,
/// </summary>
public class VOTYPESystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<VOTYPE>
{
__HANDLE_NULL__ #if NET5_0_OR_GREATER
__HANDLE_NULL__ public override bool HandleNull => true;
__HANDLE_NULL__ #endif
public override VOTYPE Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
{
return new VOTYPE(global::System.TimeOnly.Parse(reader.GetString(), global::System.Globalization.CultureInfo.InvariantCulture));
Expand Down
1 change: 1 addition & 0 deletions src/Vogen/VoWorkItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public INamedTypeSymbol UnderlyingType
public bool IsTheUnderlyingAValueType { get; init; }

public bool IsTheWrapperAValueType { get; init; }
public bool IsTheWrapperAReferenceType => !IsTheWrapperAValueType;

public List<InstanceProperties> InstanceProperties { get; init; } = new();

Expand Down
52 changes: 52 additions & 0 deletions tests/ConsumerTests/BugFixTests/BugFix624.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 System.Text.Json.Serialization.Metadata;
using SystemTextJsonSerializer = System.Text.Json.JsonSerializer;

namespace ConsumerTests.BugFixTests.BugFix624;

[ValueObject<string>(conversions: Conversions.SystemTextJson)]
public partial class NonStrictReferenceVo;

[ValueObject<string>(conversions: Conversions.SystemTextJson, deserializationStrictness: DeserializationStrictness.DisallowNulls)]
public partial class StrictReferenceVo;

[ValueObject(conversions: Conversions.SystemTextJson)]
public partial struct ValueVo;

public class StrictContainer
{
// ReSharper disable once NullableWarningSuppressionIsUsed
public StrictReferenceVo Rvo { get; set; } = null!;
}

public class NonStrictContainer
{
// ReSharper disable once NullableWarningSuppressionIsUsed
public NonStrictReferenceVo Rvo { get; set; } = null!;
}

/// <summary>
/// Fixes bug https://github.com/SteveDunn/Vogen/issues/624 where STJ
/// deserialization would allow a null value object if it was a reference type
/// </summary>
public class Tests
{
[Fact]
public void Should_throw_if_null()
{
string json = $$"""{"Rvo":null}""";

Action a = () => JsonSerializer.Deserialize<StrictContainer>(json);
a.Should().ThrowExactly<ValueObjectValidationException>().WithMessage("Cannot create a value object with null.");
}

[Fact]
public void Can_override_null_behaviour_to_not_throw()
{
string json = $$"""{"Rvo":null}""";

var x = JsonSerializer.Deserialize<NonStrictContainer>(json)!;
x.Rvo.Should().BeNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using VerifyXunit;
using Vogen;

namespace SnapshotTests.JsonNumberCustomizations;
namespace SnapshotTests.SystemTextJsonGeneration;

/// <summary>
/// These tests verify that types containing <see cref="Customizations.TreatNumberAsStringInSystemTextJson"/> are written correctly.
Expand Down Expand Up @@ -81,4 +81,5 @@ namespace Whatever
.CustomizeSettings(s => s.UseFileName(TestHelper.ShortenForFilename(className)))
.RunOnAllFrameworks();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Threading.Tasks;
using VerifyXunit;
using Vogen;

namespace SnapshotTests.SystemTextJsonGeneration;

[UsesVerify]
public class DeserializationStrictnessTests
{
[Fact]
public Task Reference_value_objects_allow_nulls_by_default()
{
var source = """
using Vogen;
namespace Whatever;
[ValueObject]
public partial class ReferenceVo;
""";

return new SnapshotRunner<ValueObjectGenerator>()
.WithSource(source)
.RunOnAllFrameworks();
}

[Fact]
public Task Can_disallow_nulls_for_reference_value_objects()
{
var source = """
using Vogen;
namespace Whatever;
[ValueObject(deserializationStrictness: DeserializationStrictness.DisallowNulls)]
public partial class ReferenceVo;
""";

return new SnapshotRunner<ValueObjectGenerator>()
.WithSource(source)
.RunOnAllFrameworks();
}

}
Loading

0 comments on commit 3bfd05c

Please sign in to comment.