Clean up JSON source gen APIs (#54527)
authorLayomi Akinrinade <laakinri@microsoft.com>
Fri, 25 Jun 2021 23:09:23 +0000 (16:09 -0700)
committerGitHub <noreply@github.com>
Fri, 25 Jun 2021 23:09:23 +0000 (16:09 -0700)
* Clean up JSON source gen APIs

* Address review feedback

* Simplify GenerateX computation

19 files changed:
src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs
src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs [moved from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs with 79% similarity]
src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs
src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs [moved from src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs with 80% similarity]
src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
src/libraries/System.Text.Json/ref/System.Text.Json.cs
src/libraries/System.Text.Json/src/System.Text.Json.csproj
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs

index 9929071..10a4be3 100644 (file)
@@ -21,6 +21,6 @@ namespace System.Text.Json.Serialization
         /// <summary>
         /// Specifies that the built-in <see cref="Json.JsonNamingPolicy.CamelCase"/> be used to convert JSON property names.
         /// </summary>
-        BuiltInCamelCase = 1
+        CamelCase = 1
     }
 }
@@ -1,7 +1,9 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+#if !BUILDING_SOURCE_GENERATOR
 using System.Text.Json.Serialization.Metadata;
+#endif
 
 namespace System.Text.Json.Serialization
 {
@@ -10,7 +12,13 @@ namespace System.Text.Json.Serialization
     /// when serializing and deserializing instances of the specified type and types in its object graph.
     /// </summary>
     [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-    public sealed class JsonSerializableAttribute : JsonAttribute
+
+#if BUILDING_SOURCE_GENERATOR
+    internal
+#else
+    public
+#endif
+    sealed class JsonSerializableAttribute : JsonAttribute
     {
         /// <summary>
         /// Initializes a new instance of <see cref="JsonSerializableAttribute"/> with the specified type.
@@ -28,7 +36,8 @@ namespace System.Text.Json.Serialization
         public string? TypeInfoPropertyName { get; set; }
 
         /// <summary>
-        /// Determines what the source generator should generate for the type.
+        /// Determines what the source generator should generate for the type. If the value is <see cref="JsonSourceGenerationMode.Default"/>,
+        /// then the setting specified on <see cref="JsonSourceGenerationOptionsAttribute.GenerationMode"/> will be used.
         /// </summary>
         public JsonSourceGenerationMode GenerationMode { get; set; }
     }
index b46292b..99785d8 100644 (file)
@@ -15,13 +15,11 @@ namespace System.Text.Json.Serialization
     enum JsonSourceGenerationMode
     {
         /// <summary>
-        /// Instructs the JSON source generator to generate serialization logic and type metadata to fallback to
-        /// when the run-time options are not compatible with the indicated <see cref="JsonSerializerOptionsAttribute"/>.
+        /// When specified on <see cref="JsonSourceGenerationOptionsAttribute.GenerationMode"/>, indicates that both type-metadata initialization logic
+        /// and optimized serialization logic should be generated for all types. When specified on <see cref="JsonSerializableAttribute.GenerationMode"/>,
+        /// indicates that the setting on <see cref="JsonSourceGenerationOptionsAttribute.GenerationMode"/> should be used.
         /// </summary>
-        /// <remarks>
-        /// This mode supports all <see cref="JsonSerializer"/> features.
-        /// </remarks>
-        MetadataAndSerialization = 0,
+        Default = 0,
 
         /// <summary>
         /// Instructs the JSON source generator to generate type-metadata initialization logic.
@@ -32,7 +30,7 @@ namespace System.Text.Json.Serialization
         Metadata = 1,
 
         /// <summary>
-        /// Instructs the JSON source generator to generate serialization logic.
+        /// Instructs the JSON source generator to generate optimized serialization logic.
         /// </summary>
         /// <remarks>
         /// This mode supports only a subset of <see cref="JsonSerializer"/> features.
@@ -13,7 +13,7 @@ namespace System.Text.Json.Serialization
 #else
     public
 #endif
-    class JsonSerializerOptionsAttribute : JsonAttribute
+    class JsonSourceGenerationOptionsAttribute : JsonAttribute
     {
         /// <summary>
         /// Specifies the default ignore condition.
@@ -43,11 +43,16 @@ namespace System.Text.Json.Serialization
         /// <summary>
         /// Specifies a built-in naming polices to convert JSON property names with.
         /// </summary>
-        public JsonKnownNamingPolicy NamingPolicy { get; set; }
+        public JsonKnownNamingPolicy PropertyNamingPolicy { get; set; }
 
         /// <summary>
         /// Specifies whether JSON output should be pretty-printed.
         /// </summary>
         public bool WriteIndented { get; set; }
+
+        /// <summary>
+        /// Specifies the source generation mode for types that don't explicitly set the mode with <see cref="JsonSerializableAttribute.GenerationMode"/>.
+        /// </summary>
+        public JsonSourceGenerationMode GenerationMode { get; set; }
     }
 }
index 7ba239f..f4b27c2 100644 (file)
@@ -13,11 +13,11 @@ namespace System.Text.Json.SourceGeneration
     /// </summary>
     internal sealed class ContextGenerationSpec
     {
-        public JsonSerializerOptionsAttribute SerializerOptions { get; init; }
+        public JsonSourceGenerationOptionsAttribute GenerationOptions { get; init; }
 
         public Type ContextType { get; init; }
 
-        public List<TypeGenerationSpec>? RootSerializableTypes { get; init; }
+        public List<TypeGenerationSpec> RootSerializableTypes { get; } = new();
 
         public List<string> ContextClassDeclarationList { get; init; }
 
index 251aa2a..e00a0ec 100644 (file)
@@ -637,10 +637,10 @@ private static {JsonPropertyInfoTypeRef}[] {propInitMethodName}({JsonSerializerC
                 bool canBeNull,
                 List<PropertyGenerationSpec>? properties)
             {
-                JsonSerializerOptionsAttribute options = _currentContext.SerializerOptions;
+                JsonSourceGenerationOptionsAttribute options = _currentContext.GenerationOptions;
 
                 // Add the property names to the context-wide cache; we'll generate the source to initialize them at the end of generation.
-                string[] runtimePropNames = GetRuntimePropNames(properties, options.NamingPolicy);
+                string[] runtimePropNames = GetRuntimePropNames(properties, options.PropertyNamingPolicy);
                 _currentContext.RuntimePropertyNames.UnionWith(runtimePropNames);
 
                 StringBuilder sb = new();
@@ -855,7 +855,7 @@ private static void {serializeMethodName}({Utf8JsonWriterTypeRef} {WriterVarName
                 {
                     runtimePropName = jsonPropName;
                 }
-                else if (namingPolicy == JsonKnownNamingPolicy.BuiltInCamelCase)
+                else if (namingPolicy == JsonKnownNamingPolicy.CamelCase)
                 {
                     runtimePropName = JsonNamingPolicy.CamelCase.ConvertName(clrPropName);
                 }
@@ -890,7 +890,7 @@ public {typeInfoPropertyTypeRef} {typeFriendlyName}
 
             private string WrapWithCheckForCustomConverterIfRequired(string source, string typeCompilableName, string typeFriendlyName, string numberHandlingNamedArg)
             {
-                if (_currentContext.SerializerOptions.IgnoreRuntimeCustomConverters)
+                if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters)
                 {
                     return source;
                 }
@@ -933,9 +933,9 @@ public {contextTypeName}({JsonSerializerOptionsTypeRef} options) : base(options,
 
             private string GetLogicForDefaultSerializerOptionsInit()
             {
-                JsonSerializerOptionsAttribute options = _currentContext.SerializerOptions;
+                JsonSourceGenerationOptionsAttribute options = _currentContext.GenerationOptions;
 
-                string? namingPolicyInit = options.NamingPolicy == JsonKnownNamingPolicy.BuiltInCamelCase
+                string? namingPolicyInit = options.PropertyNamingPolicy == JsonKnownNamingPolicy.CamelCase
                     ? $@"
             PropertyNamingPolicy = {JsonNamingPolicyTypeRef}.CamelCase"
                     : null;
@@ -953,7 +953,7 @@ private static {JsonSerializerOptionsTypeRef} {DefaultOptionsStaticVarName} {{ g
 
             private string GetFetchLogicForRuntimeSpecifiedCustomConverter()
             {
-                if (_currentContext.SerializerOptions.IgnoreRuntimeCustomConverters)
+                if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters)
                 {
                     return "";
                 }
index b909e1e..386a799 100644 (file)
@@ -98,9 +98,9 @@ namespace System.Text.Json.SourceGeneration
                 Compilation compilation = _executionContext.Compilation;
                 INamedTypeSymbol jsonSerializerContextSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerContext");
                 INamedTypeSymbol jsonSerializableAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute");
-                INamedTypeSymbol jsonSerializerOptionsAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerOptionsAttribute");
+                INamedTypeSymbol jsonSourceGenerationOptionsAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute");
 
-                if (jsonSerializerContextSymbol == null || jsonSerializableAttributeSymbol == null || jsonSerializerOptionsAttributeSymbol == null)
+                if (jsonSerializerContextSymbol == null || jsonSerializableAttributeSymbol == null || jsonSourceGenerationOptionsAttributeSymbol == null)
                 {
                     return null;
                 }
@@ -117,8 +117,8 @@ namespace System.Text.Json.SourceGeneration
                         continue;
                     }
 
-                    List<TypeGenerationSpec>? rootTypes = null;
-                    JsonSerializerOptionsAttribute? options = null;
+                    JsonSourceGenerationOptionsAttribute? options = null;
+                    List<AttributeSyntax>? serializableAttributeList = null;
 
                     foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists)
                     {
@@ -133,19 +133,15 @@ namespace System.Text.Json.SourceGeneration
 
                         if (jsonSerializableAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default))
                         {
-                            TypeGenerationSpec? metadata = GetRootSerializableType(compilationSemanticModel, attributeSyntax);
-                            if (metadata != null)
-                            {
-                                (rootTypes ??= new List<TypeGenerationSpec>()).Add(metadata);
-                            }
+                            (serializableAttributeList ??= new List<AttributeSyntax>()).Add(attributeSyntax);
                         }
-                        else if (jsonSerializerOptionsAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default))
+                        else if (jsonSourceGenerationOptionsAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default))
                         {
                             options = GetSerializerOptions(attributeSyntax);
                         }
                     }
 
-                    if (rootTypes == null)
+                    if (serializableAttributeList == null)
                     {
                         // No types were indicated with [JsonSerializable]
                         continue;
@@ -161,14 +157,29 @@ namespace System.Text.Json.SourceGeneration
                         continue;
                     }
 
-                    contextGenSpecList ??= new List<ContextGenerationSpec>();
-                    contextGenSpecList.Add(new ContextGenerationSpec
+                    ContextGenerationSpec contextGenSpec = new()
                     {
-                        SerializerOptions = options ?? new JsonSerializerOptionsAttribute(),
+                        GenerationOptions = options ?? new JsonSourceGenerationOptionsAttribute(),
                         ContextType = contextTypeSymbol.AsType(_metadataLoadContext),
-                        RootSerializableTypes = rootTypes,
                         ContextClassDeclarationList = classDeclarationList
-                    });
+                    };
+
+                    foreach(AttributeSyntax attribute in serializableAttributeList)
+                    {
+                        TypeGenerationSpec? metadata = GetRootSerializableType(compilationSemanticModel, attribute, contextGenSpec.GenerationOptions.GenerationMode);
+                        if (metadata != null)
+                        {
+                            contextGenSpec.RootSerializableTypes.Add(metadata);
+                        }
+                    }
+
+                    if (contextGenSpec.RootSerializableTypes.Count == 0)
+                    {
+                        continue;
+                    }
+
+                    contextGenSpecList ??= new List<ContextGenerationSpec>();
+                    contextGenSpecList.Add(contextGenSpec);
 
                     // Clear the cache of generated metadata between the processing of context classes.
                     _typeGenerationSpecCache.Clear();
@@ -220,11 +231,10 @@ namespace System.Text.Json.SourceGeneration
                 return match != null;
             }
 
-            private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List<string> classDeclarationList)
+            private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List<string>? classDeclarationList)
             {
-                classDeclarationList = new();
-
                 INamedTypeSymbol currentSymbol = typeSymbol;
+                classDeclarationList = null;
 
                 while (currentSymbol != null)
                 {
@@ -259,7 +269,7 @@ namespace System.Text.Json.SourceGeneration
                         declarationElements[tokenCount] = "class";
                         declarationElements[tokenCount + 1] = currentSymbol.Name;
 
-                        classDeclarationList.Add(string.Join(" ", declarationElements));
+                        (classDeclarationList ??= new List<string>()).Add(string.Join(" ", declarationElements));
                     }
 
                     currentSymbol = currentSymbol.ContainingType;
@@ -269,13 +279,15 @@ namespace System.Text.Json.SourceGeneration
                 return true;
             }
 
-            private TypeGenerationSpec? GetRootSerializableType(SemanticModel compilationSemanticModel, AttributeSyntax attributeSyntax)
+            private TypeGenerationSpec? GetRootSerializableType(
+                SemanticModel compilationSemanticModel,
+                AttributeSyntax attributeSyntax,
+                JsonSourceGenerationMode generationMode)
             {
                 IEnumerable<SyntaxNode> attributeArguments = attributeSyntax.DescendantNodes().Where(node => node is AttributeArgumentSyntax);
 
                 ITypeSymbol? typeSymbol = null;
                 string? typeInfoPropertyName = null;
-                JsonSourceGenerationMode generationMode = default;
 
                 bool seenFirstArg = false;
                 foreach (AttributeArgumentSyntax node in attributeArguments)
@@ -298,15 +310,20 @@ namespace System.Text.Json.SourceGeneration
                         NameEqualsSyntax? propertyNameNode = childNodes.First() as NameEqualsSyntax;
                         Debug.Assert(propertyNameNode != null);
 
-                        SyntaxNode? propertyValueMode = childNodes.ElementAtOrDefault(1);
-                        if (propertyNameNode.Name.Identifier.ValueText == "TypeInfoPropertyName")
+                        SyntaxNode? propertyValueNode = childNodes.ElementAtOrDefault(1);
+                        string optionName = propertyNameNode.Name.Identifier.ValueText;
+
+                        if (optionName == nameof(JsonSerializableAttribute.TypeInfoPropertyName))
                         {
-                            typeInfoPropertyName = propertyValueMode.GetFirstToken().ValueText;
+                            typeInfoPropertyName = propertyValueNode.GetFirstToken().ValueText;
                         }
-                        else
+                        else if (optionName == nameof(JsonSerializableAttribute.GenerationMode))
                         {
-                            Debug.Assert(propertyNameNode.Name.Identifier.ValueText == "GenerationMode");
-                            generationMode = (JsonSourceGenerationMode)Enum.Parse(typeof(JsonSourceGenerationMode), propertyValueMode.GetLastToken().ValueText);
+                            JsonSourceGenerationMode? mode = GetJsonSourceGenerationModeEnumVal(propertyValueNode);
+                            if (mode.HasValue)
+                            {
+                                generationMode = mode.Value;
+                            }
                         }
                     }
                 }
@@ -325,37 +342,49 @@ namespace System.Text.Json.SourceGeneration
                     return null;
                 }
 
-                TypeGenerationSpec typeGenerationSpec = GetOrAddTypeGenerationSpec(type);
+                TypeGenerationSpec typeGenerationSpec = GetOrAddTypeGenerationSpec(type, generationMode);
 
                 if (typeInfoPropertyName != null)
                 {
                     typeGenerationSpec.TypeInfoPropertyName = typeInfoPropertyName;
                 }
 
-                ClassType classType = typeGenerationSpec.ClassType;
-                CollectionType collectionType = typeGenerationSpec.CollectionType;
-                switch (generationMode)
+                if (generationMode != default)
                 {
-                    case JsonSourceGenerationMode.MetadataAndSerialization:
-                        break;
-                    case JsonSourceGenerationMode.Metadata:
-                        typeGenerationSpec.GenerateSerializationLogic = false;
-                        break;
-                    case JsonSourceGenerationMode.Serialization:
-                        typeGenerationSpec.GenerateMetadata = false;
-                        break;
-                    default:
-                        throw new InvalidOperationException();
+                    typeGenerationSpec.GenerationMode = generationMode;
                 }
 
                 return typeGenerationSpec;
             }
 
-            private static JsonSerializerOptionsAttribute? GetSerializerOptions(AttributeSyntax attributeSyntax)
+            private static JsonSourceGenerationMode? GetJsonSourceGenerationModeEnumVal(SyntaxNode propertyValueMode)
             {
+                IEnumerable<string> enumTokens = propertyValueMode
+                    .DescendantTokens()
+                    .Where(token => IsValidEnumIdentifier(token.ValueText))
+                    .Select(token => token.ValueText);
+                string enumAsStr = string.Join(",", enumTokens);
+
+                if (Enum.TryParse<JsonSourceGenerationMode>(enumAsStr, out JsonSourceGenerationMode value))
+                {
+                    return value;
+                }
+
+                return null;
+
+                static bool IsValidEnumIdentifier(string token) => token != nameof(JsonSourceGenerationMode) && token != "." && token != "|";
+            }
+
+            private static JsonSourceGenerationOptionsAttribute? GetSerializerOptions(AttributeSyntax? attributeSyntax)
+            {
+                if (attributeSyntax == null)
+                {
+                    return null;
+                }
+
                 IEnumerable<SyntaxNode> attributeArguments = attributeSyntax.DescendantNodes().Where(node => node is AttributeArgumentSyntax);
 
-                JsonSerializerOptionsAttribute options = new();
+                JsonSourceGenerationOptionsAttribute options = new();
 
                 foreach (AttributeArgumentSyntax node in attributeArguments)
                 {
@@ -369,26 +398,70 @@ namespace System.Text.Json.SourceGeneration
 
                     switch (propertyNameNode.Name.Identifier.ValueText)
                     {
-                        case "DefaultIgnoreCondition":
-                            options.DefaultIgnoreCondition = (JsonIgnoreCondition)Enum.Parse(typeof(JsonIgnoreCondition), propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.DefaultIgnoreCondition):
+                            {
+                                if (Enum.TryParse<JsonIgnoreCondition>(propertyValueStr, out JsonIgnoreCondition value))
+                                {
+                                    options.DefaultIgnoreCondition = value;
+                                }
+                            }
                             break;
-                        case "IgnoreReadOnlyFields":
-                            options.IgnoreReadOnlyFields = bool.Parse(propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.IgnoreReadOnlyFields):
+                            {
+                                if (bool.TryParse(propertyValueStr, out bool value))
+                                {
+                                    options.IgnoreReadOnlyFields = value;
+                                }
+                            }
                             break;
-                        case "IgnoreReadOnlyProperties":
-                            options.IgnoreReadOnlyProperties = bool.Parse(propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.IgnoreReadOnlyProperties):
+                            {
+                                if (bool.TryParse(propertyValueStr, out bool value))
+                                {
+                                    options.IgnoreReadOnlyProperties = value;
+                                }
+                            }
+                            break;
+                        case nameof(JsonSourceGenerationOptionsAttribute.IgnoreRuntimeCustomConverters):
+                            {
+                                if (bool.TryParse(propertyValueStr, out bool value))
+                                {
+                                    options.IgnoreRuntimeCustomConverters = value;
+                                }
+                            }
                             break;
-                        case "IgnoreRuntimeCustomConverters":
-                            options.IgnoreRuntimeCustomConverters = bool.Parse(propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.IncludeFields):
+                            {
+                                if (bool.TryParse(propertyValueStr, out bool value))
+                                {
+                                    options.IncludeFields = value;
+                                }
+                            }
                             break;
-                        case "IncludeFields":
-                            options.IncludeFields = bool.Parse(propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.PropertyNamingPolicy):
+                            {
+                                if (Enum.TryParse<JsonKnownNamingPolicy>(propertyValueStr, out JsonKnownNamingPolicy value))
+                                {
+                                    options.PropertyNamingPolicy = value;
+                                }
+                            }
                             break;
-                        case "NamingPolicy":
-                            options.NamingPolicy = (JsonKnownNamingPolicy)Enum.Parse(typeof(JsonKnownNamingPolicy), propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.WriteIndented):
+                            {
+                                if (bool.TryParse(propertyValueStr, out bool value))
+                                {
+                                    options.WriteIndented = value;
+                                }
+                            }
                             break;
-                        case "WriteIndented":
-                            options.WriteIndented = bool.Parse(propertyValueStr);
+                        case nameof(JsonSourceGenerationOptionsAttribute.GenerationMode):
+                            {
+                                JsonSourceGenerationMode? mode = GetJsonSourceGenerationModeEnumVal(propertyValueNode);
+                                if (mode.HasValue)
+                                {
+                                    options.GenerationMode = mode.Value;
+                                }
+                            }
                             break;
                         default:
                             throw new InvalidOperationException();
@@ -398,7 +471,7 @@ namespace System.Text.Json.SourceGeneration
                 return options;
             }
 
-            private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type)
+            private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGenerationMode generationMode)
             {
                 if (_typeGenerationSpecCache.TryGetValue(type, out TypeGenerationSpec? typeMetadata))
                 {
@@ -514,7 +587,7 @@ namespace System.Text.Json.SourceGeneration
 
                         foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags))
                         {
-                            PropertyGenerationSpec metadata = GetPropertyGenerationSpec(propertyInfo);
+                            PropertyGenerationSpec metadata = GetPropertyGenerationSpec(propertyInfo, generationMode);
 
                             // Ignore indexers.
                             if (propertyInfo.GetIndexParameters().Length > 0)
@@ -530,7 +603,7 @@ namespace System.Text.Json.SourceGeneration
 
                         foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags))
                         {
-                            PropertyGenerationSpec metadata = GetPropertyGenerationSpec(fieldInfo);
+                            PropertyGenerationSpec metadata = GetPropertyGenerationSpec(fieldInfo, generationMode);
 
                             if (metadata.CanUseGetter || metadata.CanUseSetter)
                             {
@@ -541,6 +614,7 @@ namespace System.Text.Json.SourceGeneration
                 }
 
                 typeMetadata.Initialize(
+                    generationMode,
                     typeRef: type.GetUniqueCompilableTypeName(),
                     typeInfoPropertyName: type.GetFriendlyTypeName(),
                     type,
@@ -549,16 +623,16 @@ namespace System.Text.Json.SourceGeneration
                     numberHandling,
                     propertiesMetadata,
                     collectionType,
-                    collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeGenerationSpec(collectionKeyType) : null,
-                    collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeGenerationSpec(collectionValueType) : null,
+                    collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeGenerationSpec(collectionKeyType, generationMode) : null,
+                    collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeGenerationSpec(collectionValueType, generationMode) : null,
                     constructionStrategy,
-                    nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeGenerationSpec(nullableUnderlyingType) : null,
+                    nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeGenerationSpec(nullableUnderlyingType, generationMode) : null,
                     converterInstatiationLogic);
 
                 return typeMetadata;
             }
 
-            private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo)
+            private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo, JsonSourceGenerationMode generationMode)
             {
                 IList<CustomAttributeData> attributeDataList = CustomAttributeData.GetCustomAttributes(memberInfo);
 
@@ -669,7 +743,7 @@ namespace System.Text.Json.SourceGeneration
                     DefaultIgnoreCondition = ignoreCondition,
                     NumberHandling = numberHandling,
                     HasJsonInclude = hasJsonInclude,
-                    TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType),
+                    TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType, generationMode),
                     DeclaringTypeRef = $"global::{memberInfo.DeclaringType.GetUniqueCompilableTypeName()}",
                     ConverterInstantiationLogic = converterInstantiationLogic
                 };
index d49dfd0..0966d4e 100644 (file)
@@ -33,7 +33,6 @@ namespace System.Text.Json.SourceGeneration
         /// <param name="executionContext"></param>
         public void Execute(GeneratorExecutionContext executionContext)
         {
-            //if (!Diagnostics.Debugger.IsAttached) { Diagnostics.Debugger.Launch(); };
             SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver;
             List<ClassDeclarationSyntax>? contextClasses = receiver.ClassDeclarationSyntaxList;
             if (contextClasses == null)
index 5ed7281..ec46f79 100644 (file)
@@ -28,8 +28,9 @@
     <Compile Include="..\Common\JsonIgnoreCondition.cs" Link="Common\System\Text\Json\Serialization\JsonIgnoreCondition.cs" />
     <Compile Include="..\Common\JsonKnownNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKnownNamingPolicy.cs" />
     <Compile Include="..\Common\JsonNumberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonNumberHandling.cs" />
-    <Compile Include="..\Common\JsonSerializerOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSerializerOptionsAttribute.cs" />
+    <Compile Include="..\Common\JsonSerializableAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSerializableAttribute.cs" />
     <Compile Include="..\Common\JsonSourceGenerationMode.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationMode.cs" />
+    <Compile Include="..\Common\JsonSourceGenerationOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationOptionsAttribute.cs" />
     <Compile Include="ClassType.cs" />
     <Compile Include="CollectionType.cs" />
     <Compile Include="JsonSourceGenerator.cs" />
index 20155f0..0d457cd 100644 (file)
@@ -23,14 +23,11 @@ namespace System.Text.Json.SourceGeneration
         /// </summary>
         public string TypeInfoPropertyName { get; set; }
 
-        public bool GenerateMetadata { get; set; } = true;
+        public JsonSourceGenerationMode GenerationMode { get; set; }
 
-        private bool? _generateSerializationLogic;
-        public bool GenerateSerializationLogic
-        {
-            get => _generateSerializationLogic ??= FastPathIsSupported();
-            set => _generateSerializationLogic = value;
-        }
+        public bool GenerateMetadata => GenerationModeIsSpecified(JsonSourceGenerationMode.Metadata);
+
+        public bool GenerateSerializationLogic => GenerationModeIsSpecified(JsonSourceGenerationMode.Serialization) && FastPathIsSupported();
 
         public Type Type { get; private set; }
 
@@ -57,6 +54,7 @@ namespace System.Text.Json.SourceGeneration
         public string? ConverterInstantiationLogic { get; private set; }
 
         public void Initialize(
+            JsonSourceGenerationMode generationMode,
             string typeRef,
             string typeInfoPropertyName,
             Type type,
@@ -71,6 +69,7 @@ namespace System.Text.Json.SourceGeneration
             TypeGenerationSpec? nullableUnderlyingTypeMetadata,
             string? converterInstantiationLogic)
         {
+            GenerationMode = generationMode;
             TypeRef = $"global::{typeRef}";
             TypeInfoPropertyName = typeInfoPropertyName;
             Type = type;
@@ -87,7 +86,7 @@ namespace System.Text.Json.SourceGeneration
             ConverterInstantiationLogic = converterInstantiationLogic;
         }
 
-        public bool FastPathIsSupported()
+        private bool FastPathIsSupported()
         {
             if (ClassType == ClassType.Object)
             {
@@ -106,5 +105,7 @@ namespace System.Text.Json.SourceGeneration
 
             return false;
         }
+
+        private bool GenerationModeIsSpecified(JsonSourceGenerationMode mode) => GenerationMode == JsonSourceGenerationMode.Default || (mode & GenerationMode) != 0;
     }
 }
index e68bffd..f22feb4 100644 (file)
@@ -792,7 +792,7 @@ namespace System.Text.Json.Serialization
     public enum JsonKnownNamingPolicy
     {
         Unspecified = 0,
-        BuiltInCamelCase = 1,
+        CamelCase = 1,
     }
     [System.FlagsAttribute]
     public enum JsonNumberHandling
@@ -828,21 +828,22 @@ namespace System.Text.Json.Serialization
         public abstract System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(System.Type type);
     }
     [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=false)]
-    public partial class JsonSerializerOptionsAttribute : System.Text.Json.Serialization.JsonAttribute
+    public partial class JsonSourceGenerationOptionsAttribute : System.Text.Json.Serialization.JsonAttribute
     {
-        public JsonSerializerOptionsAttribute() { }
+        public JsonSourceGenerationOptionsAttribute() { }
         public System.Text.Json.Serialization.JsonIgnoreCondition DefaultIgnoreCondition { get { throw null; } set { } }
         public bool IgnoreReadOnlyFields { get { throw null; } set { } }
         public bool IgnoreReadOnlyProperties { get { throw null; } set { } }
         public bool IgnoreRuntimeCustomConverters { get { throw null; } set { } }
         public bool IncludeFields { get { throw null; } set { } }
-        public System.Text.Json.Serialization.JsonKnownNamingPolicy NamingPolicy { get { throw null; } set { } }
+        public System.Text.Json.Serialization.JsonKnownNamingPolicy PropertyNamingPolicy { get { throw null; } set { } }
         public bool WriteIndented { get { throw null; } set { } }
+        public JsonSourceGenerationMode GenerationMode { get { throw null; } set { } }
     }
     [System.FlagsAttribute]
     public enum JsonSourceGenerationMode
     {
-        MetadataAndSerialization = 0,
+        Default = 0,
         Metadata = 1,
         Serialization = 2,
     }
index bc957ca..142d9f8 100644 (file)
@@ -25,8 +25,9 @@
     <Compile Include="..\Common\JsonIgnoreCondition.cs" Link="Common\System\Text\Json\Serialization\JsonIgnoreCondition.cs" />
     <Compile Include="..\Common\JsonKnownNamingPolicy.cs" Link="Common\System\Text\Json\Serialization\JsonKnownNamingPolicy.cs" />
     <Compile Include="..\Common\JsonNumberHandling.cs" Link="Common\System\Text\Json\Serialization\JsonNumberHandling.cs" />
-    <Compile Include="..\Common\JsonSerializerOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSerializerOptionsAttribute.cs" />
+    <Compile Include="..\Common\JsonSerializableAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSerializableAttribute.cs" />
     <Compile Include="..\Common\JsonSourceGenerationMode.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationMode.cs" />
+    <Compile Include="..\Common\JsonSourceGenerationOptionsAttribute.cs" Link="Common\System\Text\Json\Serialization\JsonSourceGenerationOptionsAttribute.cs" />
     <Compile Include="System\Text\Json\BitStack.cs" />
     <Compile Include="System\Text\Json\Document\JsonDocument.cs" />
     <Compile Include="System\Text\Json\Document\JsonDocument.DbRow.cs" />
@@ -89,7 +90,6 @@
     <Compile Include="System\Text\Json\Serialization\Attributes\JsonIncludeAttribute.cs" />
     <Compile Include="System\Text\Json\Serialization\Attributes\JsonNumberHandlingAttribute.cs" />
     <Compile Include="System\Text\Json\Serialization\Attributes\JsonPropertyNameAttribute.cs" />
-    <Compile Include="System\Text\Json\Serialization\Attributes\JsonSerializableAttribute.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonMetadataServicesConverter.cs" />
     <Compile Include="System\Text\Json\Serialization\IgnoreReferenceResolver.cs" />
     <Compile Include="System\Text\Json\Serialization\JsonSerializerContext.cs" />
index 77f723b..a302d92 100644 (file)
@@ -84,7 +84,7 @@ namespace System.Text.Json.Serialization
         /// Creates an instance of <see cref="JsonSerializerContext"/> and binds it with the indicated <see cref="JsonSerializerOptions"/>.
         /// </summary>
         /// <param name="instanceOptions">The run-time provided options for the context instance.</param>
-        /// <param name="defaultOptions">The default run-time options for the context. It's values are defined at design-time via <see cref="JsonSerializerOptionsAttribute"/>.</param>
+        /// <param name="defaultOptions">The default run-time options for the context. It's values are defined at design-time via <see cref="JsonSourceGenerationOptionsAttribute"/>.</param>
         /// <remarks>
         /// If no instance options are passed, then no options are set until the context is bound using <see cref="JsonSerializerOptions.AddContext{TContext}"/>,
         /// or until <see cref="Options"/> is called, where a new options instance is created and bound.
index 12f3717..e0f846d 100644 (file)
@@ -15,7 +15,7 @@ namespace System.Text.Json.Serialization.Metadata
         /// <param name="options">The <see cref="JsonSerializerOptions"/> to use.</param>
         /// <param name="elementInfo">A <see cref="JsonTypeInfo"/> instance representing the element type.</param>
         /// <param name="numberHandling">The <see cref="JsonNumberHandling"/> option to apply to number collection elements.</param>
-        /// <param name="serializeFunc">An optimized serialization implementation assuming pre-determined <see cref="JsonSerializerOptionsAttribute"/> defaults.</param>
+        /// <param name="serializeFunc">An optimized serialization implementation assuming pre-determined <see cref="JsonSourceGenerationOptionsAttribute"/> defaults.</param>
         /// <returns></returns>
         public static JsonTypeInfo<TElement[]> CreateArrayInfo<TElement>(
             JsonSerializerOptions options,
@@ -40,7 +40,7 @@ namespace System.Text.Json.Serialization.Metadata
         /// <param name="createObjectFunc">A <see cref="Func{TResult}"/> to create an instance of the list when deserializing.</param>
         /// <param name="elementInfo">A <see cref="JsonTypeInfo"/> instance representing the element type.</param>
         /// <param name="numberHandling">The <see cref="JsonNumberHandling"/> option to apply to number collection elements.</param>
-        /// <param name="serializeFunc">An optimized serialization implementation assuming pre-determined <see cref="JsonSerializerOptionsAttribute"/> defaults.</param>
+        /// <param name="serializeFunc">An optimized serialization implementation assuming pre-determined <see cref="JsonSourceGenerationOptionsAttribute"/> defaults.</param>
         /// <returns></returns>
         public static JsonTypeInfo<TCollection> CreateListInfo<TCollection, TElement>(
             JsonSerializerOptions options,
@@ -69,7 +69,7 @@ namespace System.Text.Json.Serialization.Metadata
         /// <param name="keyInfo">A <see cref="JsonTypeInfo"/> instance representing the key type.</param>
         /// <param name="valueInfo">A <see cref="JsonTypeInfo"/> instance representing the value type.</param>
         /// <param name="numberHandling">The <see cref="JsonNumberHandling"/> option to apply to number collection elements.</param>
-        /// <param name="serializeFunc">An optimized serialization implementation assuming pre-determined <see cref="JsonSerializerOptionsAttribute"/> defaults.</param>
+        /// <param name="serializeFunc">An optimized serialization implementation assuming pre-determined <see cref="JsonSourceGenerationOptionsAttribute"/> defaults.</param>
         /// <returns></returns>
         public static JsonTypeInfo<TCollection> CreateDictionaryInfo<TCollection, TKey, TValue>(
             JsonSerializerOptions options,
index 14fd403..5268573 100644 (file)
@@ -93,7 +93,7 @@ namespace System.Text.Json.Serialization.Metadata
         /// <param name="options">The <see cref="JsonSerializerOptions"/> to initialize the metadata with.</param>
         /// <param name="createObjectFunc">Provides a mechanism to create an instance of the class or struct when deserializing.</param>
         /// <param name="propInitFunc">Provides a mechanism to initialize metadata for properties and fields of the class or struct.</param>
-        /// <param name="serializeFunc">Provides a serialization implementation for instances of the class or struct which assumes options specified by <see cref="JsonSerializerOptionsAttribute"/>.</param>
+        /// <param name="serializeFunc">Provides a serialization implementation for instances of the class or struct which assumes options specified by <see cref="JsonSourceGenerationOptionsAttribute"/>.</param>
         /// <param name="numberHandling">Specifies how number properties and fields should be processed when serializing and deserializing.</param>
         /// <typeparam name="T">The type of the class or struct.</typeparam>
         /// <exception cref="InvalidOperationException">Thrown when <paramref name="options"/> and <paramref name="propInitFunc"/> are both null.</exception>
index 208ac31..13bdbc8 100644 (file)
@@ -24,7 +24,7 @@ namespace System.Text.Json.Serialization.Metadata
 
         /// <summary>
         /// A method that serializes an instance of <typeparamref name="T"/> using
-        /// <see cref="JsonSerializerOptionsAttribute"/> values specified at design time.
+        /// <see cref="JsonSourceGenerationOptionsAttribute"/> values specified at design time.
         /// </summary>
         public Action<Utf8JsonWriter, T>? Serialize { get; private protected set; }
     }
index 5d4b469..0bb57ab 100644 (file)
@@ -23,6 +23,55 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
     [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata)]
     [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata)]
+    internal partial class MetadataWithPerTypeAttributeContext : JsonSerializerContext, ITestContext
+    {
+    }
+
+    public sealed class MetadataWithPerTypeAttributeContextTests : RealWorldContextTests
+    {
+        public MetadataWithPerTypeAttributeContextTests() : base(MetadataWithPerTypeAttributeContext.Default, (options) => new MetadataWithPerTypeAttributeContext(options)) { }
+
+        [Fact]
+        public override void EnsureFastPathGeneratedAsExpected()
+        {
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.Location.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.RepeatedLocation.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.IndexViewModel.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.EmptyPoco.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTemps.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyType2.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyIntermediateType.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.HighLowTempsImmutable.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedClass.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.Serialize);
+            Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize);
+        }
+    }
+
+    [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
+    [JsonSerializable(typeof(Location))]
+    [JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation")]
+    [JsonSerializable(typeof(ActiveOrUpcomingEvent))]
+    [JsonSerializable(typeof(CampaignSummaryViewModel))]
+    [JsonSerializable(typeof(IndexViewModel))]
+    [JsonSerializable(typeof(WeatherForecastWithPOCOs))]
+    [JsonSerializable(typeof(EmptyPoco))]
+    [JsonSerializable(typeof(HighLowTemps))]
+    [JsonSerializable(typeof(MyType))]
+    [JsonSerializable(typeof(MyType2))]
+    [JsonSerializable(typeof(MyIntermediateType))]
+    [JsonSerializable(typeof(HighLowTempsImmutable))]
+    [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))]
+    [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))]
+    [JsonSerializable(typeof(object[]))]
+    [JsonSerializable(typeof(string))]
+    [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
     internal partial class MetadataContext : JsonSerializerContext, ITestContext
     {
     }
index a92759a..f8317b5 100644 (file)
@@ -14,15 +14,15 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(WeatherForecastWithPOCOs), GenerationMode = JsonSourceGenerationMode.Metadata)]
     [JsonSerializable(typeof(EmptyPoco), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(HighLowTemps), GenerationMode = JsonSourceGenerationMode.Serialization)]
-    [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)]
-    [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)]
-    [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)]
+    [JsonSerializable(typeof(MyType), GenerationMode = JsonSourceGenerationMode.Default)]
+    [JsonSerializable(typeof(MyType2), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
+    [JsonSerializable(typeof(MyIntermediateType), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(HighLowTempsImmutable), GenerationMode = JsonSourceGenerationMode.Metadata)]
     [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
-    [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)]
-    [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.MetadataAndSerialization)]
+    [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
+    [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
     internal partial class MixedModeContext : JsonSerializerContext, ITestContext
     {
     }
@@ -60,7 +60,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.IndexViewModel);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.IndexViewModel), typeof(CampaignSummaryViewModel));
 
-            IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).IndexViewModel);
+            IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel);
             VerifyIndexViewModel(expected, obj);
         }
 
@@ -72,7 +72,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.CampaignSummaryViewModel);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.CampaignSummaryViewModel), typeof(CampaignSummaryViewModel));
 
-            CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel);
+            CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel);
             VerifyCampaignSummaryViewModel(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.CampaignSummaryViewModel);
@@ -86,7 +86,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.WeatherForecastWithPOCOs);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.WeatherForecastWithPOCOs), typeof(HighLowTemps));
 
-            WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).WeatherForecastWithPOCOs);
+            WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).WeatherForecastWithPOCOs);
             VerifyWeatherForecastWithPOCOs(expected, obj);
         }
 
@@ -98,7 +98,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.EmptyPoco);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.EmptyPoco), typeof(EmptyPoco));
 
-            EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).EmptyPoco);
+            EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).EmptyPoco);
             VerifyEmptyPoco(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.EmptyPoco);
@@ -112,7 +112,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.RepeatedLocation);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.RepeatedLocation), typeof(RepeatedTypes.Location));
 
-            RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).RepeatedLocation);
+            RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).RepeatedLocation);
             VerifyRepeatedLocation(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.RepeatedLocation);
@@ -122,11 +122,11 @@ namespace System.Text.Json.SourceGeneration.Tests
         public override void HandlesNestedTypes()
         {
             string json = @"{""MyInt"":5}";
-            MyNestedClass obj = JsonSerializer.Deserialize<MyNestedClass>(json, ((ITestContext)MetadataContext.Default).MyNestedClass);
+            MyNestedClass obj = JsonSerializer.Deserialize<MyNestedClass>(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedClass);
             Assert.Equal(5, obj.MyInt);
             Assert.Equal(json, JsonSerializer.Serialize(obj, DefaultContext.MyNestedClass));
 
-            MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize<MyNestedClass.MyNestedNestedClass>(json, ((ITestContext)MetadataContext.Default).MyNestedNestedClass);
+            MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize<MyNestedClass.MyNestedNestedClass>(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedNestedClass);
             Assert.Equal(5, obj2.MyInt);
             Assert.Equal(json, JsonSerializer.Serialize(obj2, DefaultContext.MyNestedNestedClass));
         }
@@ -138,12 +138,12 @@ namespace System.Text.Json.SourceGeneration.Tests
             CampaignSummaryViewModel campaignSummary = CreateCampaignSummaryViewModel();
 
             string json = JsonSerializer.Serialize(new object[] { index, campaignSummary }, DefaultContext.ObjectArray);
-            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray);
+            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray);
 
             JsonElement indexAsJsonElement = (JsonElement)arr[0];
             JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1];
-            VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).IndexViewModel));
-            VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel));
+            VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel));
+            VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel));
         }
 
         [Fact]
@@ -156,7 +156,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             Assert.Same(JsonNamingPolicy.CamelCase, ((JsonSerializerContext)context).Options.PropertyNamingPolicy);
 
             string json = JsonSerializer.Serialize(new object[] { index, campaignSummary }, context.ObjectArray);
-            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray);
+            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray);
 
             JsonElement indexAsJsonElement = (JsonElement)arr[0];
             JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1];
index 45ce3f3..02567c4 100644 (file)
@@ -6,6 +6,28 @@ using Xunit;
 
 namespace System.Text.Json.SourceGeneration.Tests
 {
+    [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)]
+    [JsonSerializable(typeof(Location))]
+    [JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation")]
+    [JsonSerializable(typeof(ActiveOrUpcomingEvent))]
+    [JsonSerializable(typeof(CampaignSummaryViewModel))]
+    [JsonSerializable(typeof(IndexViewModel))]
+    [JsonSerializable(typeof(WeatherForecastWithPOCOs))]
+    [JsonSerializable(typeof(EmptyPoco))]
+    [JsonSerializable(typeof(HighLowTemps))]
+    [JsonSerializable(typeof(MyType))]
+    [JsonSerializable(typeof(MyType2))]
+    [JsonSerializable(typeof(MyIntermediateType))]
+    [JsonSerializable(typeof(HighLowTempsImmutable))]
+    [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass))]
+    [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))]
+    [JsonSerializable(typeof(object[]))]
+    [JsonSerializable(typeof(string))]
+    [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
+    internal partial class SerializationContext : JsonSerializerContext, ITestContext
+    {
+    }
+
     [JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(RepeatedTypes.Location), GenerationMode = JsonSourceGenerationMode.Serialization, TypeInfoPropertyName = "RepeatedLocation")]
     [JsonSerializable(typeof(ActiveOrUpcomingEvent), GenerationMode = JsonSourceGenerationMode.Serialization)]
@@ -23,11 +45,11 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Serialization)]
-    internal partial class SerializationContext : JsonSerializerContext, ITestContext
+    internal partial class SerializationWithPerTypeAttributeContext : JsonSerializerContext, ITestContext
     {
     }
 
-    [JsonSerializerOptions(NamingPolicy = JsonKnownNamingPolicy.BuiltInCamelCase)]
+    [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
     [JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(RepeatedTypes.Location), GenerationMode = JsonSourceGenerationMode.Serialization, TypeInfoPropertyName = "RepeatedLocation")]
     [JsonSerializable(typeof(ActiveOrUpcomingEvent), GenerationMode = JsonSourceGenerationMode.Serialization)]
@@ -49,9 +71,14 @@ namespace System.Text.Json.SourceGeneration.Tests
     {
     }
 
-    public sealed class SerializationContextTests : RealWorldContextTests
+    public class SerializationContextTests : RealWorldContextTests
     {
-        public SerializationContextTests() : base(SerializationContext.Default, (options) => new SerializationContext(options)) { }
+        public SerializationContextTests() : this(SerializationContext.Default, (options) => new SerializationContext(options)) { }
+
+        internal SerializationContextTests(ITestContext defaultContext, Func<JsonSerializerOptions, ITestContext> contextCreator)
+            : base(defaultContext, contextCreator)
+        {
+        }
 
         [Fact]
         public override void EnsureFastPathGeneratedAsExpected()
@@ -83,7 +110,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.Location);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.Location), typeof(Location));
 
-            Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).Location);
+            Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).Location);
             VerifyLocation(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.Location);
@@ -97,7 +124,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.IndexViewModel);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.IndexViewModel), typeof(IndexViewModel));
 
-            IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).IndexViewModel);
+            IndexViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel);
             VerifyIndexViewModel(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.IndexViewModel);
@@ -111,7 +138,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.CampaignSummaryViewModel);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.CampaignSummaryViewModel), typeof(CampaignSummaryViewModel));
 
-            CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel);
+            CampaignSummaryViewModel obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel);
             VerifyCampaignSummaryViewModel(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.CampaignSummaryViewModel);
@@ -125,7 +152,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.ActiveOrUpcomingEvent);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.ActiveOrUpcomingEvent), typeof(ActiveOrUpcomingEvent));
 
-            ActiveOrUpcomingEvent obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ActiveOrUpcomingEvent);
+            ActiveOrUpcomingEvent obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ActiveOrUpcomingEvent);
             VerifyActiveOrUpcomingEvent(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.ActiveOrUpcomingEvent);
@@ -139,7 +166,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.WeatherForecastWithPOCOs);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.WeatherForecastWithPOCOs), typeof(WeatherForecastWithPOCOs));
 
-            WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).WeatherForecastWithPOCOs);
+            WeatherForecastWithPOCOs obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).WeatherForecastWithPOCOs);
             VerifyWeatherForecastWithPOCOs(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.WeatherForecastWithPOCOs);
@@ -153,7 +180,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.EmptyPoco);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.EmptyPoco), typeof(EmptyPoco));
 
-            EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).EmptyPoco);
+            EmptyPoco obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).EmptyPoco);
             VerifyEmptyPoco(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.EmptyPoco);
@@ -167,7 +194,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             string json = JsonSerializer.Serialize(expected, DefaultContext.RepeatedLocation);
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.RepeatedLocation), typeof(RepeatedTypes.Location));
 
-            RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).RepeatedLocation);
+            RepeatedTypes.Location obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).RepeatedLocation);
             VerifyRepeatedLocation(expected, obj);
 
             AssertFastPathLogicCorrect(json, obj, DefaultContext.RepeatedLocation);
@@ -178,12 +205,12 @@ namespace System.Text.Json.SourceGeneration.Tests
         {
             MyType myType = new() { Type = new() };
             string json = JsonSerializer.Serialize(myType, DefaultContext.MyType);
-            myType = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyType);
+            myType = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyType);
             AssertFastPathLogicCorrect(json, myType, DefaultContext.MyType);
 
             MyType2 myType2 = new() { Type = new MyIntermediateType() { Type = myType } };
             json = JsonSerializer.Serialize(myType2, DefaultContext.MyType2);
-            myType2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyType2);
+            myType2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyType2);
             AssertFastPathLogicCorrect(json, myType2, DefaultContext.MyType2);
         }
 
@@ -194,12 +221,12 @@ namespace System.Text.Json.SourceGeneration.Tests
             CampaignSummaryViewModel campaignSummary = CreateCampaignSummaryViewModel();
 
             string json = JsonSerializer.Serialize(new object[] { index, campaignSummary }, DefaultContext.ObjectArray);
-            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray);
+            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray);
 
             JsonElement indexAsJsonElement = (JsonElement)arr[0];
             JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1];
-            VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).IndexViewModel));
-            VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataContext.Default).CampaignSummaryViewModel));
+            VerifyIndexViewModel(index, JsonSerializer.Deserialize(indexAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).IndexViewModel));
+            VerifyCampaignSummaryViewModel(campaignSummary, JsonSerializer.Deserialize(campaignSummeryAsJsonElement.GetRawText(), ((ITestContext)MetadataWithPerTypeAttributeContext.Default).CampaignSummaryViewModel));
         }
 
         [Fact]
@@ -218,7 +245,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             Assert.Contains("description", json);
             Assert.Contains("organizationName", json);
 
-            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ObjectArray);
+            object[] arr = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ObjectArray);
 
             JsonElement indexAsJsonElement = (JsonElement)arr[0];
             JsonElement campaignSummeryAsJsonElement = (JsonElement)arr[1];
@@ -235,7 +262,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             ITestContext context = new SerializationContext(options);
 
             string json = JsonSerializer.Serialize(new object[] { "Hello", "World" }, typeof(object[]), (JsonSerializerContext)context);
-            object[] arr = (object[])JsonSerializer.Deserialize(json, typeof(object[]), (JsonSerializerContext)((ITestContext)MetadataContext.Default));
+            object[] arr = (object[])JsonSerializer.Deserialize(json, typeof(object[]), (JsonSerializerContext)((ITestContext)MetadataWithPerTypeAttributeContext.Default));
 
             JsonElement hello = (JsonElement)arr[0];
             JsonElement world = (JsonElement)arr[1];
@@ -247,11 +274,11 @@ namespace System.Text.Json.SourceGeneration.Tests
         public override void HandlesNestedTypes()
         {
             string json = @"{""MyInt"":5}";
-            MyNestedClass obj = JsonSerializer.Deserialize<MyNestedClass>(json, ((ITestContext)MetadataContext.Default).MyNestedClass);
+            MyNestedClass obj = JsonSerializer.Deserialize<MyNestedClass>(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedClass);
             Assert.Equal(5, obj.MyInt);
             Assert.Equal(json, JsonSerializer.Serialize(obj, DefaultContext.MyNestedClass));
 
-            MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize<MyNestedClass.MyNestedNestedClass>(json, ((ITestContext)MetadataContext.Default).MyNestedNestedClass);
+            MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize<MyNestedClass.MyNestedNestedClass>(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedNestedClass);
             Assert.Equal(5, obj2.MyInt);
             Assert.Equal(json, JsonSerializer.Serialize(obj2, DefaultContext.MyNestedNestedClass));
         }
@@ -265,7 +292,7 @@ namespace System.Text.Json.SourceGeneration.Tests
             void RunTest(ClassWithEnumAndNullable expected)
             {
                 string json = JsonSerializer.Serialize(expected, DefaultContext.ClassWithEnumAndNullable);
-                ClassWithEnumAndNullable actual = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).ClassWithEnumAndNullable);
+                ClassWithEnumAndNullable actual = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).ClassWithEnumAndNullable);
                 Assert.Equal(expected.Day, actual.Day);
                 Assert.Equal(expected.NullableDay, actual.NullableDay);
             }
@@ -281,4 +308,31 @@ namespace System.Text.Json.SourceGeneration.Tests
             JsonTestHelper.AssertThrows_PropMetadataInit(() => JsonSerializer.Deserialize(json, DefaultContext.HighLowTempsImmutable), typeof(HighLowTempsImmutable));
         }
     }
+
+    public sealed class SerializationWithPerTypeAttributeContextTests : SerializationContextTests
+    {
+        public SerializationWithPerTypeAttributeContextTests() : base(SerializationWithPerTypeAttributeContext.Default, (options) => new SerializationContext(options)) { }
+
+        [Fact]
+        public override void EnsureFastPathGeneratedAsExpected()
+        {
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.Location.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.RepeatedLocation.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ActiveOrUpcomingEvent.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.CampaignSummaryViewModel.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.IndexViewModel.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.WeatherForecastWithPOCOs.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTemps.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyType2.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyIntermediateType.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.HighLowTempsImmutable.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedClass.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize);
+            Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.Serialize);
+            Assert.Null(SerializationWithPerTypeAttributeContext.Default.String.Serialize);
+            Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize);
+        }
+    }
 }