--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Text.Json
+{
+ internal static partial class JsonConstants
+ {
+ public const string GlobalNamespaceValue = "<global namespace>";
+
+ public const string SystemTextJsonSourceGenerationName = "System.Text.Json.SourceGeneration";
+
+ public const string IJsonOnSerializedFullName = "System.Text.Json.Serialization.IJsonOnSerialized";
+ public const string IJsonOnSerializingFullName = "System.Text.Json.Serialization.IJsonOnSerializing";
+ }
+}
id: "SYSLIB1030",
title: new LocalizableResourceString(nameof(SR.TypeNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
messageFormat: new LocalizableResourceString(nameof(SR.TypeNotSupportedMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
- category: SystemTextJsonSourceGenerationName,
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
id: "SYSLIB1031",
title: new LocalizableResourceString(nameof(SR.DuplicateTypeNameTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
messageFormat: new LocalizableResourceString(nameof(SR.DuplicateTypeNameMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
- category: SystemTextJsonSourceGenerationName,
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
int declarationCount = declarationList.Count;
Debug.Assert(declarationCount >= 1);
- StringBuilder sb = new();
+ string @namespace = _currentContext.ContextType.Namespace;
+ bool isInGlobalNamespace = @namespace == JsonConstants.GlobalNamespaceValue;
+
+ StringBuilder sb = new("// <auto-generated/>");
- sb.Append($@"// <auto-generated/>
+ if (!isInGlobalNamespace)
+ {
+ sb.Append(@$"
-namespace {_currentContext.ContextType.Namespace}
+namespace {@namespace}
{{");
+ }
for (int i = 0; i < declarationCount - 1; i++)
{
sb.AppendLine(IndentSource("}", numIndentations: declarationCount + i + 1));
}
- // Match curly brace for namespace.
- sb.AppendLine("}");
+ if (!isInGlobalNamespace)
+ {
+ sb.AppendLine("}");
+ }
_executionContext.AddSource(fileName, SourceText.From(sb.ToString(), Encoding.UTF8));
}
// Begin method logic.
if (typeGenSpec.ImplementsIJsonOnSerializing)
{
- sb.Append($@"(({IJsonOnSerializingFullName}){ValueVarName}).OnSerializing();");
+ sb.Append($@"((global::{JsonConstants.IJsonOnSerializingFullName}){ValueVarName}).OnSerializing();");
sb.Append($@"{Environment.NewLine} ");
}
if (typeGenSpec.ImplementsIJsonOnSerialized)
{
sb.Append($@"{Environment.NewLine} ");
- sb.Append($@"(({IJsonOnSerializedFullName}){ValueVarName}).OnSerialized();");
+ sb.Append($@"((global::{JsonConstants.IJsonOnSerializedFullName}){ValueVarName}).OnSerialized();");
};
return GenerateFastPathFuncForType(serializeMethodName, typeRef, sb.ToString(), typeGenSpec.CanBeNull);
id: "SYSLIB1032",
title: new LocalizableResourceString(nameof(SR.ContextClassesMustBePartialTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
messageFormat: new LocalizableResourceString(nameof(SR.ContextClassesMustBePartialMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
- category: SystemTextJsonSourceGenerationName,
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
id: "SYSLIB1033",
title: new LocalizableResourceString(nameof(SR.MultipleJsonConstructorAttributeTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
messageFormat: new LocalizableResourceString(nameof(SR.MultipleJsonConstructorAttributeFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
- category: SystemTextJsonSourceGenerationName,
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
Type type = typeSymbol.AsType(_metadataLoadContext);
- if (type.Namespace == "<global namespace>")
- {
- // typeof() reference where the type's name isn't fully qualified.
- // The compilation is not valid and the user needs to fix their code.
- // The compiler will notify the user so we don't have to.
- return null;
- }
-
TypeGenerationSpec typeGenerationSpec = GetOrAddTypeGenerationSpec(type, generationMode);
if (typeInfoPropertyName != null)
// GetInterface() is currently not implemented, so we use GetInterfaces().
IEnumerable<string> interfaces = type.GetInterfaces().Select(interfaceType => interfaceType.FullName!);
- implementsIJsonOnSerialized = interfaces.FirstOrDefault(interfaceName => interfaceName == IJsonOnSerializedFullName) != null;
- implementsIJsonOnSerializing = interfaces.FirstOrDefault(interfaceName => interfaceName == IJsonOnSerializingFullName) != null;
+ implementsIJsonOnSerialized = interfaces.FirstOrDefault(interfaceName => interfaceName == JsonConstants.IJsonOnSerializedFullName) != null;
+ implementsIJsonOnSerializing = interfaces.FirstOrDefault(interfaceName => interfaceName == JsonConstants.IJsonOnSerializingFullName) != null;
propGenSpecList = new List<PropertyGenerationSpec>();
Dictionary<string, PropertyGenerationSpec>? ignoredMembers = null;
[Generator]
public sealed partial class JsonSourceGenerator : ISourceGenerator
{
- private const string SystemTextJsonSourceGenerationName = "System.Text.Json.SourceGeneration";
- private const string IJsonOnSerializedFullName = "System.Text.Json.Serialization.IJsonOnSerialized";
- private const string IJsonOnSerializingFullName = "System.Text.Json.Serialization.IJsonOnSerializing";
-
/// <summary>
/// Registers a syntax resolver to receive compilation units.
/// </summary>
using System.Globalization;
using System.Linq;
using System.Reflection;
+using System.Text.Json;
using Microsoft.CodeAnalysis;
namespace System.Text.Json.Reflection
sb.Insert(0, $"{currentSymbol.Name}+");
}
- if (!string.IsNullOrWhiteSpace(Namespace))
+ if (!string.IsNullOrWhiteSpace(Namespace) && Namespace != JsonConstants.GlobalNamespaceValue)
{
sb.Insert(0, $"{Namespace}.");
}
<Compile Include="..\Common\ReflectionExtensions.cs" Link="Common\System\Text\Json\Serialization\ReflectionExtensions.cs" />
<Compile Include="ClassType.cs" />
<Compile Include="CollectionType.cs" />
+ <Compile Include="JsonConstants.cs" />
<Compile Include="JsonSourceGenerator.cs" />
<Compile Include="JsonSourceGenerator.Emitter.cs" />
<Compile Include="JsonSourceGenerator.Parser.cs" />
CheckCompilationDiagnosticsErrors(newCompilation.GetDiagnostics());
}
+ [Fact]
+ public void ContextTypeNotInNamespace()
+ {
+ string source = @"
+ using System.Text.Json.Serialization;
+
+ [JsonSerializable(typeof(MyType))]
+ internal partial class JsonContext : JsonSerializerContext
+ {
+ }
+
+ public class MyType
+ {
+ public int PublicPropertyInt { get; set; }
+ public string PublicPropertyString { get; set; }
+ private int PrivatePropertyInt { get; set; }
+ private string PrivatePropertyString { get; set; }
+
+ public double PublicDouble;
+ public char PublicChar;
+ private double PrivateDouble;
+ private char PrivateChar;
+
+ public void MyMethod() { }
+ public void MySecondMethod() { }
+
+ public void UsePrivates()
+ {
+ PrivateDouble = 0;
+ PrivateChar = ' ';
+ double d = PrivateDouble;
+ char c = PrivateChar;
+ }
+ }";
+
+ Compilation compilation = CompilationHelper.CreateCompilation(source);
+
+ JsonSourceGenerator generator = new JsonSourceGenerator();
+
+ Compilation newCompilation = CompilationHelper.RunGenerators(compilation, out ImmutableArray<Diagnostic> generatorDiags, generator);
+
+ // Make sure compilation was successful.
+ CheckCompilationDiagnosticsErrors(generatorDiags);
+ CheckCompilationDiagnosticsErrors(newCompilation.GetDiagnostics());
+
+ Dictionary<string, Type> types = generator.GetSerializableTypes();
+
+ // Check base functionality of found types.
+ Assert.Equal(1, types.Count);
+ Type myType = types["MyType"];
+ Assert.Equal("MyType", myType.FullName);
+
+ // Check for received fields, properties and methods in created type.
+ string[] expectedPropertyNames = { "PublicPropertyInt", "PublicPropertyString", };
+ string[] expectedFieldNames = { "PublicChar", "PublicDouble" };
+ string[] expectedMethodNames = { "get_PrivatePropertyInt", "get_PrivatePropertyString", "get_PublicPropertyInt", "get_PublicPropertyString", "MyMethod", "MySecondMethod", "set_PrivatePropertyInt", "set_PrivatePropertyString", "set_PublicPropertyInt", "set_PublicPropertyString", "UsePrivates" };
+ CheckFieldsPropertiesMethods(myType, expectedFieldNames, expectedPropertyNames, expectedMethodNames);
+ }
+
private void CheckCompilationDiagnosticsErrors(ImmutableArray<Diagnostic> diagnostics)
{
Assert.Empty(diagnostics.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error));