Skip to content

ComplexValueObject with nullable SmartEnum property throws ArgumentNullException during JSON serialization when the property value is null #19

@roman-kms

Description

@roman-kms

Bug: ComplexValueObject with nullable SmartEnum property throws ArgumentNullException during JSON serialization when the
property value is null.

Version: Thinktecture.Runtime.Extensions 10.1.0

Root cause: The generated ValueObjectJsonConverter.Write method calls _kindConverter.Write(writer, null, options) directly
when the nullable property value is null. The condition if (!ignoreNullValues || propertyValue is not null) only skips
null values when ignoreNullValues is true — with the default options it is false, so the condition is always true.
ThinktectureSpanParsableJsonConverter.Write has no null guard and throws.

Expected: Either the generated Write method emits writer.WriteNullValue() when the property value is null, or
ThinktectureSpanParsableJsonConverter.Write handles null by writing a JSON null.

Stack trace:
System.ArgumentNullException: Value cannot be null. (Parameter 'value')
at Thinktecture.Text.Json.Serialization.ThinktectureSpanParsableJsonConverter`2.Write(Utf8JsonWriter writer, T value,
JsonSerializerOptions options)
at SampleComplexValueObject.ValueObjectJsonConverter.Write(Utf8JsonWriter writer, SampleComplexValueObject value,
JsonSerializerOptions options)

Reproduction: A [ComplexValueObject] with a nullable [SmartEnum] property. When the Smart Enum property is null and
JsonSerializerOptions.DefaultIgnoreCondition is JsonIgnoreCondition.Never (the default), calling
JsonSerializer.Serialize(...) throws ArgumentNullException: Value cannot be null. (Parameter 'value').

using System.Text.Json;
using Shouldly;
using Thinktecture;

[SmartEnum<string>]
public partial class SampleKind
{
  public static readonly SampleKind A = new("A");
  public static readonly SampleKind B = new("B");
}

[ComplexValueObject]
public partial class SampleComplexValueObject
{
  public int RequiredValue { get; }
  public SampleKind? NullableKind { get; }
}

public static class ComplexValueObjectJsonSerializationTests
{
  [Fact]
  public static void Should_serialize_complex_value_object_with_null_smart_enum_without_throwing()
  {
    var value = SampleComplexValueObject.Create(42, null);

    var act = () => JsonSerializer.Serialize(value);

    act.ShouldNotThrow();
  }

  [Fact]
  public static void Should_roundtrip_complex_value_object_with_null_smart_enum()
  {
    var value = SampleComplexValueObject.Create(42, null);

    var json = JsonSerializer.Serialize(value);
    var result = JsonSerializer.Deserialize<SampleComplexValueObject>(json);

    result.ShouldNotBeNull();
    result.RequiredValue.ShouldBe(42);
    result.NullableKind.ShouldBeNull();
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions