Skip to content

Commit bea16c8

Browse files
committed
ValueObjectMessageFormatterResolver prioritizes MessagePackFormatterAttribute over its own formatter
1 parent c9f9a10 commit bea16c8

File tree

3 files changed

+106
-18
lines changed

3 files changed

+106
-18
lines changed

src/Thinktecture.Runtime.Extensions.Json/Text/Json/Serialization/ValueObjectJsonConverterFactory.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,21 @@ public override bool CanConvert(Type typeToConvert)
9191
if (valueObjectType is null)
9292
return false;
9393

94-
return !_skipValueObjectsWithJsonConverterAttribute || !valueObjectType.GetCustomAttributes<JsonConverterAttribute>().Any();
94+
return !_skipValueObjectsWithJsonConverterAttribute || valueObjectType.GetCustomAttribute<JsonConverterAttribute>() is null;
9595
}
9696

9797
private static Type? GetValueObjectType(Type typeToConvert)
9898
{
99+
// typeToConvert could be derived type (like nested Smart Enum)
100+
var metadata = KeyedValueObjectMetadataLookup.Find(typeToConvert);
101+
102+
if (metadata is not null)
103+
return metadata.Type;
104+
99105
if (typeToConvert.GetCustomAttributes<ValueObjectFactoryAttribute>().Any(a => a.UseForSerialization.HasFlag(SerializationFrameworks.SystemTextJson)))
100106
return typeToConvert;
101107

102-
return KeyedValueObjectMetadataLookup.Find(typeToConvert)?.Type;
108+
return null;
103109
}
104110

105111
/// <inheritdoc />

src/Thinktecture.Runtime.Extensions.MessagePack/ValueObjectMessageFormatterResolver.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,33 @@ public class ValueObjectMessageFormatterResolver : IFormatterResolver
1616
/// </summary>
1717
public static readonly IFormatterResolver Instance = new ValueObjectMessageFormatterResolver();
1818

19+
private readonly bool _skipValueObjectsWithMessagePackFormatterAttribute;
20+
21+
/// <summary>
22+
/// Initializes new instance of <see cref="ValueObjectMessageFormatterResolver"/>.
23+
/// </summary>
24+
public ValueObjectMessageFormatterResolver()
25+
: this(true)
26+
{
27+
}
28+
29+
/// <summary>
30+
/// Initializes new instance of <see cref="ValueObjectMessageFormatterResolver"/>.
31+
/// </summary>
32+
/// <param name="skipValueObjectsWithMessagePackFormatterAttribute">
33+
/// Indication whether to skip value objects with <see cref="MessagePackFormatterAttribute"/>.
34+
/// </param>
35+
public ValueObjectMessageFormatterResolver(bool skipValueObjectsWithMessagePackFormatterAttribute)
36+
{
37+
_skipValueObjectsWithMessagePackFormatterAttribute = skipValueObjectsWithMessagePackFormatterAttribute;
38+
}
39+
1940
/// <inheritdoc />
2041
public IMessagePackFormatter<T>? GetFormatter<T>()
2142
{
43+
if (_skipValueObjectsWithMessagePackFormatterAttribute && Cache<T>.HasMessagePackFormatterAttribute)
44+
return null;
45+
2246
var formatter = Cache<T>.Formatter;
2347

2448
if (formatter != null)
@@ -34,8 +58,10 @@ private static class Cache<T>
3458
{
3559
public static readonly IMessagePackFormatter<T>? Formatter;
3660

37-
// ReSharper disable once StaticMemberInGenericType
61+
// ReSharper disable StaticMemberInGenericType
62+
public static readonly bool HasMessagePackFormatterAttribute;
3863
public static readonly string? InitError;
64+
// ReSharper restore StaticMemberInGenericType
3965

4066
static Cache()
4167
{
@@ -81,6 +107,7 @@ static Cache()
81107
}
82108

83109
Formatter = (IMessagePackFormatter<T>)formatter;
110+
HasMessagePackFormatterAttribute = type.GetCustomAttribute<MessagePackFormatterAttribute>() is not null;
84111
}
85112
}
86113
}

test/Thinktecture.Runtime.Extensions.MessagePack.Tests/Formatters/EnumMessagePackFormatterTests/RoundtripSerialize.cs

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ public void Should_roundtrip_serialize_int_based_enum_having_formatter()
7272
}
7373

7474
public static IEnumerable<object[]> DataForValueObject => new[]
75-
{
76-
new object[] { new ClassWithIntBasedEnum(IntegerEnum.Item1) },
77-
new object[] { new ClassWithStringBasedEnum(TestEnum.Item1) },
78-
new object[] { TestEnum.Item1 },
79-
new object[] { IntegerEnum.Item1 }
80-
};
75+
{
76+
new object[] { new ClassWithIntBasedEnum(IntegerEnum.Item1) },
77+
new object[] { new ClassWithStringBasedEnum(TestEnum.Item1) },
78+
new object[] { TestEnum.Item1 },
79+
new object[] { IntegerEnum.Item1 }
80+
};
8181

8282
[Theory]
8383
[MemberData(nameof(DataForValueObject))]
@@ -95,15 +95,15 @@ private void RoundTripSerializeWithCustomOptions<T>(T value)
9595
}
9696

9797
public static IEnumerable<object[]> DataForValueObjectWithMultipleProperties => new[]
98-
{
99-
new object[] { null },
100-
new object[] { ValueObjectWithMultipleProperties.Create(0, null, null!) },
101-
new object[] { ValueObjectWithMultipleProperties.Create(0, null, null!) },
102-
new object[] { ValueObjectWithMultipleProperties.Create(0, 0, String.Empty) },
103-
new object[] { ValueObjectWithMultipleProperties.Create(1, 42, "Value") },
104-
new object[] { ValueObjectWithMultipleProperties.Create(1, 42, "Value") },
105-
new object[] { ValueObjectWithMultipleProperties.Create(1, 42, "Value") }
106-
};
98+
{
99+
new object[] { null },
100+
new object[] { ValueObjectWithMultipleProperties.Create(0, null, null!) },
101+
new object[] { ValueObjectWithMultipleProperties.Create(0, null, null!) },
102+
new object[] { ValueObjectWithMultipleProperties.Create(0, 0, String.Empty) },
103+
new object[] { ValueObjectWithMultipleProperties.Create(1, 42, "Value") },
104+
new object[] { ValueObjectWithMultipleProperties.Create(1, 42, "Value") },
105+
new object[] { ValueObjectWithMultipleProperties.Create(1, 42, "Value") }
106+
};
107107

108108
[Theory]
109109
[MemberData(nameof(DataForValueObjectWithMultipleProperties))]
@@ -182,4 +182,59 @@ public void Should_deserialize_complex_value_object_having_custom_factory()
182182

183183
value.Should().BeEquivalentTo(BoundaryWithCustomFactoryNames.Get(1, 2));
184184
}
185+
186+
public static IEnumerable<object[]> ObjectWithStructTestData =
187+
[
188+
[new TestClass<IntBasedStructValueObject>(IntBasedStructValueObject.Create(42))],
189+
[new TestClass<IntBasedStructValueObject?>(IntBasedStructValueObject.Create(42))],
190+
[new TestClass<IntBasedReferenceValueObject>(IntBasedReferenceValueObject.Create(42))],
191+
[new TestStruct<IntBasedStructValueObject>(IntBasedStructValueObject.Create(42))],
192+
[new TestStruct<IntBasedStructValueObject?>(IntBasedStructValueObject.Create(42))],
193+
[new TestStruct<IntBasedReferenceValueObject>(IntBasedReferenceValueObject.Create(42))],
194+
];
195+
196+
[Theory]
197+
[MemberData(nameof(ObjectWithStructTestData))]
198+
public void Should_roundtrip_serialize_types_with_struct_properties_using_resolver(object obj)
199+
{
200+
Roundtrip_serialize_types_with_struct_properties_using_resolver(true, obj);
201+
Roundtrip_serialize_types_with_struct_properties_using_resolver(false, obj);
202+
}
203+
204+
private static void Roundtrip_serialize_types_with_struct_properties_using_resolver(
205+
bool skipValueObjectsWithMessagePackFormatter,
206+
object obj)
207+
{
208+
var resolver = CompositeResolver.Create(new ValueObjectMessageFormatterResolver(skipValueObjectsWithMessagePackFormatter), StandardResolver.Instance);
209+
var options = MessagePackSerializerOptions.Standard.WithResolver(resolver);
210+
211+
var bytes = MessagePackSerializer.Serialize(obj, options, CancellationToken.None);
212+
var value = MessagePackSerializer.Deserialize(obj.GetType(), bytes, options, CancellationToken.None);
213+
214+
value.Should().BeEquivalentTo(obj);
215+
}
216+
217+
[MessagePackObject]
218+
public struct TestClass<T>
219+
{
220+
[Key(0)]
221+
public T Prop { get; set; }
222+
223+
public TestClass(T prop)
224+
{
225+
Prop = prop;
226+
}
227+
}
228+
229+
[MessagePackObject]
230+
public struct TestStruct<T>
231+
{
232+
[Key(0)]
233+
public T Prop { get; set; }
234+
235+
public TestStruct(T prop)
236+
{
237+
Prop = prop;
238+
}
239+
}
185240
}

0 commit comments

Comments
 (0)