-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Add support for setting a different JsonConverter on properties
- Loading branch information
Showing
5 changed files
with
142 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Reflection; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace OptionalValues; | ||
|
||
/// <summary> | ||
/// When placed on a property or field of type <see cref="OptionalValue{T}"/>, specifies the converter type to use for <c>T</c>. | ||
/// </summary> | ||
/// <remarks> | ||
/// The specified converter type must derive from JsonConverter. | ||
/// </remarks> | ||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] | ||
[SuppressMessage("Performance", "CA1813:Avoid unsealed attributes", Justification = "This attribute should be inheritable to allow custom initialization logic.")] | ||
public class OptionalValueJsonConverterAttribute : JsonConverterAttribute | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of <see cref="OptionalValueJsonConverterAttribute"/> with the specified inner converter type. | ||
/// </summary> | ||
/// <param name="innerConverterType"></param> | ||
public OptionalValueJsonConverterAttribute(Type innerConverterType) | ||
{ | ||
InnerConverterType = innerConverterType; | ||
} | ||
|
||
/// <summary> | ||
/// Protected constructor for derived classes, allowing to create custom logic for creating the inner converter. | ||
/// </summary> | ||
protected OptionalValueJsonConverterAttribute() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Gets the type of the inner converter. This can be null if <see cref="CreateInnerConverter"/> is overridden to provide custom logic for creating the inner converter. | ||
/// </summary> | ||
public Type? InnerConverterType { get; } | ||
|
||
/// <inheritdoc /> | ||
public override JsonConverter? CreateConverter(Type typeToConvert) | ||
{ | ||
JsonConverter innerConverter = CreateInnerConverter(); | ||
|
||
return (JsonConverter?)Activator.CreateInstance( | ||
type: typeof(OptionalValueJsonConverterFactory), | ||
bindingAttr: BindingFlags.NonPublic | BindingFlags.Instance, | ||
args: [innerConverter], | ||
binder: null, | ||
culture: null | ||
); | ||
} | ||
|
||
/// <summary> | ||
/// Creates the inner converter. This method can be overridden in derived classes to provide custom logic for creating the inner converter. | ||
/// </summary> | ||
/// <returns>A <see cref="JsonConverter{T}"/> or <see cref="JsonConverterFactory"/></returns> | ||
protected virtual JsonConverter CreateInnerConverter() | ||
{ | ||
if (InnerConverterType is null) | ||
{ | ||
throw new InvalidOperationException("When inheriting from OptionalValueJsonConverterAttribute, the CreateInnerConverter method must be overridden."); | ||
} | ||
return (JsonConverter)Activator.CreateInstance(InnerConverterType)!; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
OptionalValues.OptionalValueJsonConverterAttribute | ||
OptionalValues.OptionalValueJsonConverterAttribute.InnerConverterType.get -> System.Type? | ||
OptionalValues.OptionalValueJsonConverterAttribute.OptionalValueJsonConverterAttribute() -> void | ||
OptionalValues.OptionalValueJsonConverterAttribute.OptionalValueJsonConverterAttribute(System.Type! innerConverterType) -> void | ||
override OptionalValues.OptionalValueJsonConverterAttribute.CreateConverter(System.Type! typeToConvert) -> System.Text.Json.Serialization.JsonConverter? | ||
virtual OptionalValues.OptionalValueJsonConverterAttribute.CreateInnerConverter() -> System.Text.Json.Serialization.JsonConverter! |
43 changes: 43 additions & 0 deletions
43
test/OptionalValues.Tests/OptionalValueJsonConverterAttributeTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace OptionalValues.Tests; | ||
|
||
public class OptionalValueJsonConverterAttributeTest | ||
{ | ||
public class ExampleModel | ||
{ | ||
[OptionalValueJsonConverter(typeof(JsonStringEnumConverter<ExampleEnum>))] | ||
public OptionalValue<ExampleEnum> EnumValue { get; set; } | ||
} | ||
|
||
public enum ExampleEnum | ||
{ | ||
Foo, | ||
Bar | ||
} | ||
|
||
private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions() | ||
.AddOptionalValueSupport(); | ||
|
||
[Fact] | ||
public void ShouldSerializeWithCustomConverterOnProperty() | ||
{ | ||
var model = new ExampleModel | ||
{ | ||
EnumValue = new OptionalValue<ExampleEnum>(ExampleEnum.Foo) | ||
}; | ||
|
||
var json = JsonSerializer.Serialize(model, JsonSerializerOptions); | ||
Assert.Equal("""{"EnumValue":"Foo"}""", json); | ||
} | ||
|
||
[Fact] | ||
public void ShouldDeserializeWithCustomConverterOnProperty() | ||
{ | ||
var json = """{"EnumValue":"Bar"}"""; | ||
ExampleModel model = JsonSerializer.Deserialize<ExampleModel>(json, JsonSerializerOptions)!; | ||
|
||
Assert.Equal(ExampleEnum.Bar, model.EnumValue.Value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters