Skip to content

Commit 14a8157

Browse files
committed
Add new generation option WithNullableSerializers to avoid Nullable<T> related issues in Unity. Issue #121.
This commit add WithNullableSerializers property to generator configuration classes and implements forcing nullable generator generation even if they are considered as built-in serializers.
1 parent c328af7 commit 14a8157

File tree

5 files changed

+244
-0
lines changed

5 files changed

+244
-0
lines changed

src/MsgPack/Serialization/ISerializerGeneratorConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ internal interface ISerializerGeneratorConfiguration
7171
/// </value>
7272
bool PreferReflectionBasedSerializer { get; set; }
7373

74+
/// <summary>
75+
/// Gets or sets a value indicating whether creating Nullable of T serializers for value type serializers.
76+
/// </summary>
77+
/// <value>
78+
/// <c>true</c> if creates Nullable of T serializers for value type serializers; otherwise, <c>false</c>.
79+
/// </value>
80+
bool WithNullableSerializers { get; set; }
81+
7482
/// <summary>
7583
/// Validates this instance state.
7684
/// </summary>

src/MsgPack/Serialization/SerializerAssemblyGenerationConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ public EnumSerializationMethod EnumSerializationMethod
132132
/// </value>
133133
public bool PreferReflectionBasedSerializer { get; set; }
134134

135+
/// <summary>
136+
/// Gets or sets a value indicating whether creating Nullable of T serializers for value type serializers.
137+
/// </summary>
138+
/// <value>
139+
/// <c>true</c> if creates Nullable of T serializers for value type serializers; otherwise, <c>false</c>.
140+
/// </value>
141+
public bool WithNullableSerializers { get; set; }
142+
135143
/// <summary>
136144
/// Initializes a new instance of the <see cref="SerializerAssemblyGenerationConfiguration"/> class.
137145
/// </summary>

src/MsgPack/Serialization/SerializerCodeGenerationConfiguration.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,14 @@ public EnumSerializationMethod EnumSerializationMethod
192192
/// </value>
193193
public bool PreferReflectionBasedSerializer { get; set; }
194194

195+
/// <summary>
196+
/// Gets or sets a value indicating whether creating Nullable of T serializers for value type serializers.
197+
/// </summary>
198+
/// <value>
199+
/// <c>true</c> if creates Nullable of T serializers for value type serializers; otherwise, <c>false</c>.
200+
/// </value>
201+
public bool WithNullableSerializers { get; set; }
202+
195203
/// <summary>
196204
/// Gets or sets a value indicating whether the generated serializers will be internal to MsgPack library itself.
197205
/// </summary>

src/MsgPack/Serialization/SerializerGenerator.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,20 @@ private static IEnumerable<Type> ExtractElementTypes( SerializationContext conte
516516
yield return elementType;
517517
}
518518
}
519+
520+
if ( configuration.WithNullableSerializers && type.GetIsValueType() && Nullable.GetUnderlyingType( type ) == null )
521+
{
522+
// Retrun nullable companion even if they always have built-in serializers.
523+
524+
var nullableType = typeof( Nullable<> ).MakeGenericType( type );
525+
526+
yield return nullableType;
527+
528+
foreach ( var elementType in ExtractElementTypes( context, configuration, nullableType ) )
529+
{
530+
yield return elementType;
531+
}
532+
}
519533
}
520534

521535
protected abstract ISerializerCodeGenerationContext CreateGenerationContext( SerializationContext context, TConfig configuration );

test/MsgPack.UnitTest/Serialization/SerializerGeneratorTest.cs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,178 @@ public void TestGenerateSerializerSourceCodes_MemberTypesOfElementTypesNested_Ge
11161116

11171117
#endregion -- Issue 120 --
11181118

1119+
#region -- Issue 121 --
1120+
1121+
[Test]
1122+
public void TestGenerateSerializerSourceCodes_MemberTypesOfElementTypes_ValueType_WithNullable_GeneratedWithNullable()
1123+
{
1124+
var configuration = new SerializerCodeGenerationConfiguration { IsRecursive = true, PreferReflectionBasedSerializer = false, WithNullableSerializers = true };
1125+
var resultCS =
1126+
SerializerGenerator.GenerateSerializerSourceCodes(
1127+
configuration,
1128+
typeof( List<RootGeneratorTestValueObject> ),
1129+
typeof( AnotherRootGeneratorTestValueObject[] )
1130+
).ToArray();
1131+
try
1132+
{
1133+
// Assert is not polluted.
1134+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( RootGeneratorTestValueObject ) ), Is.False );
1135+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherRootGeneratorTestValueObject ) ), Is.False );
1136+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( GeneratorTestValueObject ) ), Is.False );
1137+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherGeneratorTestValueObject ) ), Is.False );
1138+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType ) ), Is.False );
1139+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType ) ), Is.False );
1140+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType? ) ), Is.False );
1141+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType? ) ), Is.False );
1142+
1143+
Assert.That( resultCS.Length, Is.EqualTo( 8 ) );
1144+
Assert.That( resultCS.Any( r => r.TargetType == typeof( RootGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1145+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1146+
Assert.That( resultCS.Any( r => r.TargetType == typeof( GeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1147+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1148+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1149+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestEnumType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1150+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1151+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestEnumType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1152+
}
1153+
finally
1154+
{
1155+
foreach ( var result in resultCS )
1156+
{
1157+
File.Delete( result.FilePath );
1158+
}
1159+
}
1160+
}
1161+
1162+
[Test]
1163+
public void TestGenerateSerializerSourceCodes_MemberTypesOfElementTypesNested_ValueType_WithNullable_GeneratedWithNullable()
1164+
{
1165+
var configuration = new SerializerCodeGenerationConfiguration { IsRecursive = true, PreferReflectionBasedSerializer = false, WithNullableSerializers = true };
1166+
var resultCS =
1167+
SerializerGenerator.GenerateSerializerSourceCodes(
1168+
configuration,
1169+
typeof( HoldsRootElementTypeValueObject )
1170+
).ToArray();
1171+
try
1172+
{
1173+
// Assert is not polluted.
1174+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( HoldsRootElementTypeValueObject ) ), Is.False );
1175+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( RootGeneratorTestValueObject ) ), Is.False );
1176+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherRootGeneratorTestValueObject ) ), Is.False );
1177+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( GeneratorTestValueObject ) ), Is.False );
1178+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherGeneratorTestValueObject ) ), Is.False );
1179+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType ) ), Is.False );
1180+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType ) ), Is.False );
1181+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType? ) ), Is.False );
1182+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType? ) ), Is.False );
1183+
1184+
Assert.That( resultCS.Length, Is.EqualTo( 9 ) );
1185+
Assert.That( resultCS.Any( r => r.TargetType == typeof( HoldsRootElementTypeValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1186+
Assert.That( resultCS.Any( r => r.TargetType == typeof( RootGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1187+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherRootGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1188+
Assert.That( resultCS.Any( r => r.TargetType == typeof( GeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1189+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1190+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1191+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestEnumType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1192+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1193+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestEnumType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1194+
}
1195+
finally
1196+
{
1197+
foreach ( var result in resultCS )
1198+
{
1199+
File.Delete( result.FilePath );
1200+
}
1201+
}
1202+
}
1203+
1204+
[Test]
1205+
public void TestGenerateSerializerSourceCodes_MemberTypesOfElementTypes_ValueType_WithoutNullable_GeneratedWithoutNullable()
1206+
{
1207+
var configuration = new SerializerCodeGenerationConfiguration { IsRecursive = true, PreferReflectionBasedSerializer = false };
1208+
Assert.That( configuration.WithNullableSerializers, Is.False );
1209+
var resultCS =
1210+
SerializerGenerator.GenerateSerializerSourceCodes(
1211+
configuration,
1212+
typeof( List<RootGeneratorTestValueObject> ),
1213+
typeof( AnotherRootGeneratorTestValueObject[] )
1214+
).ToArray();
1215+
try
1216+
{
1217+
// Assert is not polluted.
1218+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( RootGeneratorTestValueObject ) ), Is.False );
1219+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherRootGeneratorTestValueObject ) ), Is.False );
1220+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( GeneratorTestValueObject ) ), Is.False );
1221+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherGeneratorTestValueObject ) ), Is.False );
1222+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType ) ), Is.False );
1223+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType ) ), Is.False );
1224+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType? ) ), Is.False );
1225+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType? ) ), Is.False );
1226+
1227+
Assert.That( resultCS.Length, Is.EqualTo( 6 ) );
1228+
Assert.That( resultCS.Any( r => r.TargetType == typeof( RootGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1229+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1230+
Assert.That( resultCS.Any( r => r.TargetType == typeof( GeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1231+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1232+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1233+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestEnumType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1234+
Assert.That( resultCS.All( r => r.TargetType != typeof( TestType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1235+
Assert.That( resultCS.All( r => r.TargetType != typeof( TestEnumType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1236+
}
1237+
finally
1238+
{
1239+
foreach ( var result in resultCS )
1240+
{
1241+
File.Delete( result.FilePath );
1242+
}
1243+
}
1244+
}
1245+
1246+
[Test]
1247+
public void TestGenerateSerializerSourceCodes_MemberTypesOfElementTypesNested_ValueType_WithoutNullable_GeneratedWithoutNullable()
1248+
{
1249+
var configuration = new SerializerCodeGenerationConfiguration { IsRecursive = true, PreferReflectionBasedSerializer = false };
1250+
Assert.That( configuration.WithNullableSerializers, Is.False );
1251+
var resultCS =
1252+
SerializerGenerator.GenerateSerializerSourceCodes(
1253+
configuration,
1254+
typeof( HoldsRootElementTypeValueObject )
1255+
).ToArray();
1256+
try
1257+
{
1258+
// Assert is not polluted.
1259+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( HoldsRootElementTypeValueObject ) ), Is.False );
1260+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( RootGeneratorTestValueObject ) ), Is.False );
1261+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherRootGeneratorTestValueObject ) ), Is.False );
1262+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( GeneratorTestValueObject ) ), Is.False );
1263+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( AnotherGeneratorTestValueObject ) ), Is.False );
1264+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType ) ), Is.False );
1265+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType ) ), Is.False );
1266+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestType? ) ), Is.False );
1267+
Assert.That( SerializationContext.Default.ContainsSerializer( typeof( TestEnumType? ) ), Is.False );
1268+
1269+
Assert.That( resultCS.Length, Is.EqualTo( 7 ) );
1270+
Assert.That( resultCS.Any( r => r.TargetType == typeof( HoldsRootElementTypeValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1271+
Assert.That( resultCS.Any( r => r.TargetType == typeof( RootGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1272+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherRootGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1273+
Assert.That( resultCS.Any( r => r.TargetType == typeof( GeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1274+
Assert.That( resultCS.Any( r => r.TargetType == typeof( AnotherGeneratorTestValueObject ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1275+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1276+
Assert.That( resultCS.Any( r => r.TargetType == typeof( TestEnumType ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1277+
Assert.That( resultCS.All( r => r.TargetType != typeof( TestType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1278+
Assert.That( resultCS.All( r => r.TargetType != typeof( TestEnumType? ) ), String.Join( ", ", resultCS.Select( r => r.TargetType.FullName ).ToArray() ) );
1279+
}
1280+
finally
1281+
{
1282+
foreach ( var result in resultCS )
1283+
{
1284+
File.Delete( result.FilePath );
1285+
}
1286+
}
1287+
}
1288+
1289+
#endregion -- Issue 121 --
1290+
11191291
private static void TestOnWorkerAppDomain( string geneartedAssemblyFilePath, PackerCompatibilityOptions packerCompatibilityOptions, byte[] bytesValue, byte[] expectedPackedValue, TestType testType )
11201292
{
11211293
var appDomainSetUp = new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase };
@@ -1313,6 +1485,40 @@ public sealed class HoldsRootElementTypeObject
13131485
public AnotherRootGeneratorTestObject[] Array { get; set; }
13141486
}
13151487

1488+
public struct GeneratorTestValueObject
1489+
{
1490+
public TestEnumType Val { get; set; }
1491+
}
1492+
1493+
public sealed class RootGeneratorTestValueObject
1494+
{
1495+
public TestType Val { get; set; }
1496+
public GeneratorTestValueObject Child { get; set; }
1497+
}
1498+
1499+
public sealed class AnotherGeneratorTestValueObject
1500+
{
1501+
public TestEnumType Val { get; set; }
1502+
}
1503+
1504+
public sealed class AnotherRootGeneratorTestValueObject
1505+
{
1506+
public TestType Val { get; set; }
1507+
public AnotherGeneratorTestValueObject Child { get; set; }
1508+
}
1509+
1510+
public sealed class HoldsElementTypeValueObject
1511+
{
1512+
public List<GeneratorTestValueObject> List { get; set; }
1513+
public AnotherGeneratorTestValueObject[] Array { get; set; }
1514+
}
1515+
1516+
public sealed class HoldsRootElementTypeValueObject
1517+
{
1518+
public List<RootGeneratorTestValueObject> List { get; set; }
1519+
public AnotherRootGeneratorTestValueObject[] Array { get; set; }
1520+
}
1521+
13161522
[Serializable]
13171523
public enum TestEnumType
13181524
{

0 commit comments

Comments
 (0)