diff --git a/src/OptionalValues/OptionalValueJsonExtensions.cs b/src/OptionalValues/OptionalValueJsonExtensions.cs
index 2ef3f5e..24cff3e 100644
--- a/src/OptionalValues/OptionalValueJsonExtensions.cs
+++ b/src/OptionalValues/OptionalValueJsonExtensions.cs
@@ -11,6 +11,7 @@ public static class OptionalValueJsonExtensions
///
/// Modifies the provided to add support for .
///
+ /// This should prefereably be done as the last call, as it applies a modifier to the registered instances in the TypeInfoResolverChain.
/// The to modify.
/// The modified to allow for chaining.
/// Thrown when is .
@@ -18,8 +19,16 @@ public static JsonSerializerOptions AddOptionalValueSupport(this JsonSerializerO
{
ArgumentNullException.ThrowIfNull(options);
- options.TypeInfoResolver = (options.TypeInfoResolver ?? new DefaultJsonTypeInfoResolver())
- .WithAddedModifier(OptionalValueJsonTypeInfoResolverModifier.ModifyTypeInfo);
+ // If the options do not have a TypeInfoResolver, add the default one, with the modifier.
+ options.TypeInfoResolver ??= new DefaultJsonTypeInfoResolver();
+
+ // We need to add the modifier to all resolvers in the chain,
+ // because it needs to be applied to all types and it's properties.
+ for (var i = 0; i < options.TypeInfoResolverChain.Count; i++)
+ {
+ options.TypeInfoResolverChain[i] = options.TypeInfoResolverChain[i]
+ .WithAddedModifier(OptionalValueJsonTypeInfoResolverModifier.ModifyTypeInfo);
+ }
return options;
}
@@ -29,7 +38,7 @@ public static JsonSerializerOptions AddOptionalValueSupport(this JsonSerializerO
///
/// The base options to copy.
/// A new based on the provided options with support for .
- public static JsonSerializerOptions WithOptionalValueSupport(this JsonSerializerOptions options) =>
- new JsonSerializerOptions(options)
+ public static JsonSerializerOptions WithOptionalValueSupport(this JsonSerializerOptions options)
+ => new JsonSerializerOptions(options)
.AddOptionalValueSupport();
}
\ No newline at end of file
diff --git a/test/OptionalValues.Tests/OptionalValueJsonWithSourceGeneratorTest.cs b/test/OptionalValues.Tests/OptionalValueJsonWithSourceGeneratorTest.cs
new file mode 100644
index 0000000..8c90877
--- /dev/null
+++ b/test/OptionalValues.Tests/OptionalValueJsonWithSourceGeneratorTest.cs
@@ -0,0 +1,184 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace OptionalValues.Tests;
+
+public class OptionalValueJsonWithSourceGeneratorTest
+{
+ private static JsonSerializerOptions CreateOptionsSingleContext()
+ {
+ var options = new JsonSerializerOptions
+ {
+ TypeInfoResolver = OptionalValueJsonWithSourceGeneratorJsonSerializationContext.Default
+ };
+ options.AddOptionalValueSupport();
+ return options;
+ }
+
+ private static JsonSerializerOptions CreateOptionsMultipleContexts()
+ {
+ var options = new JsonSerializerOptions
+ {
+ TypeInfoResolverChain =
+ {
+ OptionalValueJsonWithSourceGeneratorJsonSerializationContext.Default,
+ OtherOptionalValueJsonWithSourceGeneratorJsonSerializationContext.Default
+ }
+ };
+ // This needs to be done last, because it will add modifiers to all resolvers in the chain.
+ options.AddOptionalValueSupport();
+ return options;
+ }
+
+ [Fact]
+ public void SerializeWithValues_ShouldWriteValues()
+ {
+ var model = new TestModel
+ {
+ Name = new OptionalValue("John"),
+ Age = new OptionalValue(42)
+ };
+
+ var options = CreateOptionsSingleContext();
+ var json = JsonSerializer.Serialize(model, options);
+
+ Assert.Equal("""{"Name":"John","Age":42}""", json);
+ }
+
+ [Fact]
+ public void SerializeWithUnspecified_ShouldNotWriteValues()
+ {
+ var model = new TestModel
+ {
+ Name = OptionalValue.Unspecified,
+ Age = OptionalValue.Unspecified
+ };
+
+ var options = CreateOptionsSingleContext();
+ var json = JsonSerializer.Serialize(model, options);
+
+ Assert.Equal("{}", json);
+ }
+
+ [Fact]
+ public void DeserializeWithValues_ShouldReadValues()
+ {
+ var json = """{"Name":"John","Age":42}""";
+
+ var options = CreateOptionsSingleContext();
+ var model = JsonSerializer.Deserialize(json, options);
+
+ Assert.Equal("John", model.Name.Value);
+ Assert.Equal(42, model.Age.Value);
+ }
+
+ [Fact]
+ public void DeserializeWithUnspecified_ShouldReadUnspecified()
+ {
+ var json = "{}";
+
+ var options = CreateOptionsSingleContext();
+ var model = JsonSerializer.Deserialize(json, options);
+
+ Assert.False(model.Name.IsSpecified);
+ Assert.False(model.Age.IsSpecified);
+ }
+
+ [Fact]
+ public void SerializeWithValuesMultipleContexts_ShouldWriteValues()
+ {
+ var model = new TestModelOtherContext
+ {
+ Test = new TestModel
+ {
+ Name = new OptionalValue("John"),
+ Age = new OptionalValue(42)
+ },
+ Street = new OptionalValue("Main Street"),
+ HouseNumber = new OptionalValue(42)
+ };
+
+ var options = CreateOptionsMultipleContexts();
+ var json = JsonSerializer.Serialize(model, options);
+
+ Assert.Equal("""{"Test":{"Name":"John","Age":42},"Street":"Main Street","HouseNumber":42}""", json);
+ }
+
+ [Fact]
+ public void SerializeWithUnspecifiedMultipleContexts_ShouldNotWriteValues()
+ {
+ var model = new TestModelOtherContext
+ {
+ Test = new TestModel
+ {
+ Name = OptionalValue.Unspecified,
+ Age = OptionalValue.Unspecified
+ },
+ Street = OptionalValue.Unspecified,
+ HouseNumber = OptionalValue.Unspecified
+ };
+
+ var options = CreateOptionsMultipleContexts();
+ var json = JsonSerializer.Serialize(model, options);
+
+ Assert.Equal("""{"Test":{}}""", json);
+ }
+
+ [Fact]
+ public void DeserializeWithValuesMultipleContexts_ShouldReadValues()
+ {
+ var json = """{"Test":{"Name":"John","Age":42},"Street":"Main Street","HouseNumber":42}""";
+
+ var options = CreateOptionsMultipleContexts();
+ var model = JsonSerializer.Deserialize(json, options);
+
+ Assert.Equal("John", model.Test.Value.Name.Value);
+ Assert.Equal(42, model.Test.Value.Age.Value);
+ Assert.Equal("Main Street", model.Street.Value);
+ Assert.Equal(42, model.HouseNumber.Value);
+ }
+
+ [Fact]
+ public void DeserializeWithUnspecifiedMultipleContexts_ShouldReadUnspecified()
+ {
+ var json = """{"Test":{}}""";
+
+ var options = CreateOptionsMultipleContexts();
+ var model = JsonSerializer.Deserialize(json, options);
+
+ Assert.True(model.Test.IsSpecified);
+
+ Assert.False(model.Test.SpecifiedValue.Name.IsSpecified);
+ Assert.False(model.Test.SpecifiedValue.Age.IsSpecified);
+ Assert.False(model.Street.IsSpecified);
+ Assert.False(model.HouseNumber.IsSpecified);
+ }
+
+ public class TestModel
+ {
+ public OptionalValue Name { get; set; }
+
+ public OptionalValue Age { get; set; }
+ }
+
+ public class TestModelOtherContext
+ {
+ public OptionalValue Test { get; set; }
+
+ public OptionalValue Street { get; set; }
+
+ public OptionalValue HouseNumber { get; set; }
+ }
+}
+
+[JsonSerializable(typeof(OptionalValueJsonWithSourceGeneratorTest.TestModel))]
+[JsonSerializable(typeof(int))]
+[JsonSerializable(typeof(string))]
+public partial class OptionalValueJsonWithSourceGeneratorJsonSerializationContext : JsonSerializerContext
+{
+}
+
+[JsonSerializable(typeof(OptionalValueJsonWithSourceGeneratorTest.TestModelOtherContext))]
+public partial class OtherOptionalValueJsonWithSourceGeneratorJsonSerializationContext : JsonSerializerContext
+{
+}
\ No newline at end of file