diff --git a/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs b/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs
index 99290715f3136..10a4be386532f 100644
--- a/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs
+++ b/src/libraries/System.Text.Json/Common/JsonKnownNamingPolicy.cs
@@ -21,6 +21,6 @@ enum JsonKnownNamingPolicy
///
/// Specifies that the built-in be used to convert JSON property names.
///
- BuiltInCamelCase = 1
+ CamelCase = 1
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs b/src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs
similarity index 79%
rename from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs
rename to src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs
index b223fada10cb9..1b689ba6cfb9a 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Attributes/JsonSerializableAttribute.cs
+++ b/src/libraries/System.Text.Json/Common/JsonSerializableAttribute.cs
@@ -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.
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public sealed class JsonSerializableAttribute : JsonAttribute
+
+#if BUILDING_SOURCE_GENERATOR
+ internal
+#else
+ public
+#endif
+ sealed class JsonSerializableAttribute : JsonAttribute
{
///
/// Initializes a new instance of with the specified type.
@@ -28,7 +36,8 @@ public JsonSerializableAttribute(Type type) { }
public string? TypeInfoPropertyName { get; set; }
///
- /// Determines what the source generator should generate for the type.
+ /// Determines what the source generator should generate for the type. If the value is ,
+ /// then the setting specified on will be used.
///
public JsonSourceGenerationMode GenerationMode { get; set; }
}
diff --git a/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs b/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs
index b46292baaeec9..99785d8be525f 100644
--- a/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs
+++ b/src/libraries/System.Text.Json/Common/JsonSourceGenerationMode.cs
@@ -15,13 +15,11 @@ namespace System.Text.Json.Serialization
enum JsonSourceGenerationMode
{
///
- /// 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 .
+ /// When specified on , indicates that both type-metadata initialization logic
+ /// and optimized serialization logic should be generated for all types. When specified on ,
+ /// indicates that the setting on should be used.
///
- ///
- /// This mode supports all features.
- ///
- MetadataAndSerialization = 0,
+ Default = 0,
///
/// Instructs the JSON source generator to generate type-metadata initialization logic.
@@ -32,7 +30,7 @@ enum JsonSourceGenerationMode
Metadata = 1,
///
- /// Instructs the JSON source generator to generate serialization logic.
+ /// Instructs the JSON source generator to generate optimized serialization logic.
///
///
/// This mode supports only a subset of features.
diff --git a/src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs
similarity index 80%
rename from src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs
rename to src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs
index a1b4d099e32ef..334e5226a5c54 100644
--- a/src/libraries/System.Text.Json/Common/JsonSerializerOptionsAttribute.cs
+++ b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs
@@ -13,7 +13,7 @@ namespace System.Text.Json.Serialization
#else
public
#endif
- class JsonSerializerOptionsAttribute : JsonAttribute
+ class JsonSourceGenerationOptionsAttribute : JsonAttribute
{
///
/// Specifies the default ignore condition.
@@ -43,11 +43,16 @@ class JsonSerializerOptionsAttribute : JsonAttribute
///
/// Specifies a built-in naming polices to convert JSON property names with.
///
- public JsonKnownNamingPolicy NamingPolicy { get; set; }
+ public JsonKnownNamingPolicy PropertyNamingPolicy { get; set; }
///
/// Specifies whether JSON output should be pretty-printed.
///
public bool WriteIndented { get; set; }
+
+ ///
+ /// Specifies the source generation mode for types that don't explicitly set the mode with .
+ ///
+ public JsonSourceGenerationMode GenerationMode { get; set; }
}
}
diff --git a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs
index 7ba239fba491d..f4b27c2db1187 100644
--- a/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs
+++ b/src/libraries/System.Text.Json/gen/ContextGenerationSpec.cs
@@ -13,11 +13,11 @@ namespace System.Text.Json.SourceGeneration
///
internal sealed class ContextGenerationSpec
{
- public JsonSerializerOptionsAttribute SerializerOptions { get; init; }
+ public JsonSourceGenerationOptionsAttribute GenerationOptions { get; init; }
public Type ContextType { get; init; }
- public List? RootSerializableTypes { get; init; }
+ public List RootSerializableTypes { get; } = new();
public List ContextClassDeclarationList { get; init; }
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
index 251aa2a18f8d6..e00a0ecfa63b2 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
@@ -637,10 +637,10 @@ private string GenerateFastPathFuncForObject(
bool canBeNull,
List? 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 string DetermineRuntimePropName(string clrPropName, string? jsonPropName
{
runtimePropName = jsonPropName;
}
- else if (namingPolicy == JsonKnownNamingPolicy.BuiltInCamelCase)
+ else if (namingPolicy == JsonKnownNamingPolicy.CamelCase)
{
runtimePropName = JsonNamingPolicy.CamelCase.ConvertName(clrPropName);
}
@@ -890,7 +890,7 @@ private string GenerateForType(TypeGenerationSpec typeMetadata, string metadataI
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 @@ private string GetRootJsonContextImplementation()
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 string GetLogicForDefaultSerializerOptionsInit()
private string GetFetchLogicForRuntimeSpecifiedCustomConverter()
{
- if (_currentContext.SerializerOptions.IgnoreRuntimeCustomConverters)
+ if (_currentContext.GenerationOptions.IgnoreRuntimeCustomConverters)
{
return "";
}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
index b909e1e42e009..386a79914cf20 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
@@ -98,9 +98,9 @@ public Parser(in GeneratorExecutionContext executionContext)
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 @@ public Parser(in GeneratorExecutionContext executionContext)
continue;
}
- List? rootTypes = null;
- JsonSerializerOptionsAttribute? options = null;
+ JsonSourceGenerationOptionsAttribute? options = null;
+ List? serializableAttributeList = null;
foreach (AttributeListSyntax attributeListSyntax in classDeclarationSyntax.AttributeLists)
{
@@ -133,19 +133,15 @@ public Parser(in GeneratorExecutionContext executionContext)
if (jsonSerializableAttributeSymbol.Equals(attributeContainingTypeSymbol, SymbolEqualityComparer.Default))
{
- TypeGenerationSpec? metadata = GetRootSerializableType(compilationSemanticModel, attributeSyntax);
- if (metadata != null)
- {
- (rootTypes ??= new List()).Add(metadata);
- }
+ (serializableAttributeList ??= new List()).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 @@ public Parser(in GeneratorExecutionContext executionContext)
continue;
}
- contextGenSpecList ??= new List();
- 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();
+ contextGenSpecList.Add(contextGenSpec);
// Clear the cache of generated metadata between the processing of context classes.
_typeGenerationSpecCache.Clear();
@@ -220,11 +231,10 @@ private bool DerivesFromJsonSerializerContext(
return match != null;
}
- private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List classDeclarationList)
+ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [NotNullWhenAttribute(true)] out List? classDeclarationList)
{
- classDeclarationList = new();
-
INamedTypeSymbol currentSymbol = typeSymbol;
+ classDeclarationList = null;
while (currentSymbol != null)
{
@@ -259,7 +269,7 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not
declarationElements[tokenCount] = "class";
declarationElements[tokenCount + 1] = currentSymbol.Name;
- classDeclarationList.Add(string.Join(" ", declarationElements));
+ (classDeclarationList ??= new List()).Add(string.Join(" ", declarationElements));
}
currentSymbol = currentSymbol.ContainingType;
@@ -269,13 +279,15 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not
return true;
}
- private TypeGenerationSpec? GetRootSerializableType(SemanticModel compilationSemanticModel, AttributeSyntax attributeSyntax)
+ private TypeGenerationSpec? GetRootSerializableType(
+ SemanticModel compilationSemanticModel,
+ AttributeSyntax attributeSyntax,
+ JsonSourceGenerationMode generationMode)
{
IEnumerable 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 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not
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 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not
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 enumTokens = propertyValueMode
+ .DescendantTokens()
+ .Where(token => IsValidEnumIdentifier(token.ValueText))
+ .Select(token => token.ValueText);
+ string enumAsStr = string.Join(",", enumTokens);
+
+ if (Enum.TryParse(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 attributeArguments = attributeSyntax.DescendantNodes().Where(node => node is AttributeArgumentSyntax);
- JsonSerializerOptionsAttribute options = new();
+ JsonSourceGenerationOptionsAttribute options = new();
foreach (AttributeArgumentSyntax node in attributeArguments)
{
@@ -369,26 +398,70 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not
switch (propertyNameNode.Name.Identifier.ValueText)
{
- case "DefaultIgnoreCondition":
- options.DefaultIgnoreCondition = (JsonIgnoreCondition)Enum.Parse(typeof(JsonIgnoreCondition), propertyValueStr);
+ case nameof(JsonSourceGenerationOptionsAttribute.DefaultIgnoreCondition):
+ {
+ if (Enum.TryParse(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(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 @@ private static bool TryGetClassDeclarationList(INamedTypeSymbol typeSymbol, [Not
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 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type)
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 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type)
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 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type)
}
typeMetadata.Initialize(
+ generationMode,
typeRef: type.GetUniqueCompilableTypeName(),
typeInfoPropertyName: type.GetFriendlyTypeName(),
type,
@@ -549,16 +623,16 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type)
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 attributeDataList = CustomAttributeData.GetCustomAttributes(memberInfo);
@@ -669,7 +743,7 @@ private PropertyGenerationSpec GetPropertyGenerationSpec(MemberInfo memberInfo)
DefaultIgnoreCondition = ignoreCondition,
NumberHandling = numberHandling,
HasJsonInclude = hasJsonInclude,
- TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType),
+ TypeGenerationSpec = GetOrAddTypeGenerationSpec(memberCLRType, generationMode),
DeclaringTypeRef = $"global::{memberInfo.DeclaringType.GetUniqueCompilableTypeName()}",
ConverterInstantiationLogic = converterInstantiationLogic
};
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
index d49dfd0e75988..0966d4eed8094 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
@@ -33,7 +33,6 @@ public void Initialize(GeneratorInitializationContext context)
///
public void Execute(GeneratorExecutionContext executionContext)
{
- //if (!Diagnostics.Debugger.IsAttached) { Diagnostics.Debugger.Launch(); };
SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver;
List? contextClasses = receiver.ClassDeclarationSyntaxList;
if (contextClasses == null)
diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
index 5ed7281c65d43..ec46f798ab378 100644
--- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
+++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
@@ -28,8 +28,9 @@
-
+
+
diff --git a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
index 20155f0e5b485..0d457cd01b196 100644
--- a/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
+++ b/src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
@@ -23,14 +23,11 @@ internal class TypeGenerationSpec
///
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 @@ public bool GenerateSerializationLogic
public string? ConverterInstantiationLogic { get; private set; }
public void Initialize(
+ JsonSourceGenerationMode generationMode,
string typeRef,
string typeInfoPropertyName,
Type type,
@@ -71,6 +69,7 @@ public void Initialize(
TypeGenerationSpec? nullableUnderlyingTypeMetadata,
string? converterInstantiationLogic)
{
+ GenerationMode = generationMode;
TypeRef = $"global::{typeRef}";
TypeInfoPropertyName = typeInfoPropertyName;
Type = type;
@@ -87,7 +86,7 @@ public void Initialize(
ConverterInstantiationLogic = converterInstantiationLogic;
}
- public bool FastPathIsSupported()
+ private bool FastPathIsSupported()
{
if (ClassType == ClassType.Object)
{
@@ -106,5 +105,7 @@ public bool FastPathIsSupported()
return false;
}
+
+ private bool GenerationModeIsSpecified(JsonSourceGenerationMode mode) => GenerationMode == JsonSourceGenerationMode.Default || (mode & GenerationMode) != 0;
}
}
diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs
index ed8880c117fea..6728e9a58c0ad 100644
--- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs
+++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs
@@ -780,7 +780,7 @@ public JsonIncludeAttribute() { }
public enum JsonKnownNamingPolicy
{
Unspecified = 0,
- BuiltInCamelCase = 1,
+ CamelCase = 1,
}
[System.FlagsAttribute]
public enum JsonNumberHandling
@@ -816,21 +816,22 @@ protected JsonSerializerContext(System.Text.Json.JsonSerializerOptions? instance
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,
}
diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
index e3ba73d6b6c23..a8826ebab70a3 100644
--- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj
+++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
@@ -25,8 +25,9 @@
-
+
+
@@ -89,7 +90,6 @@
-
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs
index 77f723b59fe66..a302d92878b9b 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs
@@ -84,7 +84,7 @@ public JsonSerializerOptions Options
/// Creates an instance of and binds it with the indicated .
///
/// The run-time provided options for the context instance.
- /// The default run-time options for the context. It's values are defined at design-time via .
+ /// The default run-time options for the context. It's values are defined at design-time via .
///
/// If no instance options are passed, then no options are set until the context is bound using ,
/// or until is called, where a new options instance is created and bound.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs
index 12f3717ddfbde..e0f846ddaf25c 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs
@@ -15,7 +15,7 @@ public static partial class JsonMetadataServices
/// The to use.
/// A instance representing the element type.
/// The option to apply to number collection elements.
- /// An optimized serialization implementation assuming pre-determined defaults.
+ /// An optimized serialization implementation assuming pre-determined defaults.
///
public static JsonTypeInfo CreateArrayInfo(
JsonSerializerOptions options,
@@ -40,7 +40,7 @@ public static JsonTypeInfo CreateArrayInfo(
/// A to create an instance of the list when deserializing.
/// A instance representing the element type.
/// The option to apply to number collection elements.
- /// An optimized serialization implementation assuming pre-determined defaults.
+ /// An optimized serialization implementation assuming pre-determined defaults.
///
public static JsonTypeInfo CreateListInfo(
JsonSerializerOptions options,
@@ -69,7 +69,7 @@ public static JsonTypeInfo CreateListInfo(
/// A instance representing the key type.
/// A instance representing the value type.
/// The option to apply to number collection elements.
- /// An optimized serialization implementation assuming pre-determined defaults.
+ /// An optimized serialization implementation assuming pre-determined defaults.
///
public static JsonTypeInfo CreateDictionaryInfo(
JsonSerializerOptions options,
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs
index 14fd4034ceabd..5268573219dd2 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.cs
@@ -93,7 +93,7 @@ public static JsonPropertyInfo CreatePropertyInfo(
/// The to initialize the metadata with.
/// Provides a mechanism to create an instance of the class or struct when deserializing.
/// Provides a mechanism to initialize metadata for properties and fields of the class or struct.
- /// Provides a serialization implementation for instances of the class or struct which assumes options specified by .
+ /// Provides a serialization implementation for instances of the class or struct which assumes options specified by .
/// Specifies how number properties and fields should be processed when serializing and deserializing.
/// The type of the class or struct.
/// Thrown when and are both null.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs
index 208ac31539c22..13bdbc8259e6c 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs
@@ -24,7 +24,7 @@ internal JsonTypeInfo()
///
/// A method that serializes an instance of using
- /// values specified at design time.
+ /// values specified at design time.
///
public Action? Serialize { get; private protected set; }
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs
index 5d4b469c8dac1..0bb57ab5721e9 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs
@@ -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
{
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs
index a92759a9ec8db..f8317b590ec28 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs
@@ -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 @@ public override void RoundTripIndexViewModel()
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 @@ public override void RoundTripCampaignSummaryViewModel()
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 @@ public override void RoundTripCollectionsDictionary()
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 @@ public override void RoundTripEmptyPoco()
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 @@ public override void RoundTripTypeNameClash()
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 @@ public override void RoundTripTypeNameClash()
public override void HandlesNestedTypes()
{
string json = @"{""MyInt"":5}";
- MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedClass);
+ MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedClass);
Assert.Equal(5, obj.MyInt);
Assert.Equal(json, JsonSerializer.Serialize(obj, DefaultContext.MyNestedClass));
- MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedNestedClass);
+ MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedNestedClass);
Assert.Equal(5, obj2.MyInt);
Assert.Equal(json, JsonSerializer.Serialize(obj2, DefaultContext.MyNestedNestedClass));
}
@@ -138,12 +138,12 @@ public override void SerializeObjectArray()
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 @@ public override void SerializeObjectArray_WithCustomOptions()
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];
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs
index 45ce3f358df37..02567c4b8ae3b 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs
@@ -6,6 +6,28 @@
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 @@ internal partial class SerializationContextWithCamelCase : JsonSerializerContext
{
}
- 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 contextCreator)
+ : base(defaultContext, contextCreator)
+ {
+ }
[Fact]
public override void EnsureFastPathGeneratedAsExpected()
@@ -83,7 +110,7 @@ public override void RoundTripLocation()
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 @@ public override void RoundTripIndexViewModel()
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 @@ public override void RoundTripCampaignSummaryViewModel()
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 @@ public override void RoundTripActiveOrUpcomingEvent()
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 @@ public override void RoundTripCollectionsDictionary()
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 @@ public override void RoundTripEmptyPoco()
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 @@ public override void RoundTripTypeNameClash()
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 @@ public override void NestedSameTypeWorks()
{
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 @@ public override void SerializeObjectArray()
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 @@ public override void SerializeObjectArray_WithCustomOptions()
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 @@ public override void SerializeObjectArray_SimpleTypes_WithCustomOptions()
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 @@ public override void SerializeObjectArray_SimpleTypes_WithCustomOptions()
public override void HandlesNestedTypes()
{
string json = @"{""MyInt"":5}";
- MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedClass);
+ MyNestedClass obj = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedClass);
Assert.Equal(5, obj.MyInt);
Assert.Equal(json, JsonSerializer.Serialize(obj, DefaultContext.MyNestedClass));
- MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataContext.Default).MyNestedNestedClass);
+ MyNestedClass.MyNestedNestedClass obj2 = JsonSerializer.Deserialize(json, ((ITestContext)MetadataWithPerTypeAttributeContext.Default).MyNestedNestedClass);
Assert.Equal(5, obj2.MyInt);
Assert.Equal(json, JsonSerializer.Serialize(obj2, DefaultContext.MyNestedNestedClass));
}
@@ -265,7 +292,7 @@ public override void EnumAndNullable()
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 @@ public override void ParameterizedConstructor()
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);
+ }
+ }
}