* Emit diagnostic warnings and exceptions when sourcegen is handling init-only inaccessible JsonInclude properties.
* add localizations for runtime exception messages
* remove exception messages from resource strings
* fix alphabetical ordering
Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Text.Json.SourceGeneration
+{
+ public sealed partial class JsonSourceGenerator
+ {
+ private sealed partial class Emitter
+ {
+ /// <summary>
+ /// Unlike sourcegen warnings, exception messages should not be localized so we keep them in source.
+ /// </summary>
+ private static class ExceptionMessages
+ {
+ public const string InaccessibleJsonIncludePropertiesNotSupported =
+ "The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.";
+
+ public const string IncompatibleConverterType =
+ "The converter '{0}' is not compatible with the type '{1}'.";
+
+ public const string InitOnlyPropertyDeserializationNotSupported =
+ "Deserialization of init-only properties is currently not supported in source generation mode.";
+
+ public const string InvalidJsonConverterFactoryOutput =
+ "The converter '{0}' cannot return null or a JsonConverterFactory instance.";
+
+ public const string InvalidSerializablePropertyConfiguration =
+ "Invalid serializable-property configuration specified for type '{0}'. For more information, see 'JsonSourceGenerationMode.Serialization'.";
+ };
+ }
+ }
+}
}}
else
{{
- throw new {InvalidOperationExceptionTypeRef}($""The converter '{{converter.GetType()}}' is not compatible with the type '{{typeToConvert}}'."");
+ throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.IncompatibleConverterType}"", converter.GetType(), typeToConvert));
}}
}}");
}
metadataInitSource.Append($@"
if (!converter.CanConvert(typeToConvert))
{{
- throw new {InvalidOperationExceptionTypeRef}($""The converter '{{converter.GetType()}}' is not compatible with the type '{{typeToConvert}}'."");
+ throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.IncompatibleConverterType}"", converter.GetType(), typeToConvert));
}}");
}
? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}"""
: "jsonPropertyName: null";
- string getterNamedArg = memberMetadata.CanUseGetter &&
- memberMetadata.DefaultIgnoreCondition != JsonIgnoreCondition.Always
- ? $"getter: static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}"
- : "getter: null";
-
- string setterNamedArg;
- if (memberMetadata.CanUseSetter &&
- memberMetadata.DefaultIgnoreCondition != JsonIgnoreCondition.Always)
+ string getterNamedArg = memberMetadata switch
{
- string propMutation = typeGenerationSpec.IsValueType
- ? @$"{UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value!"
- : $@"(({declaringTypeCompilableName})obj).{clrPropertyName} = value!";
-
- setterNamedArg = $"setter: static (obj, value) => {propMutation}";
- }
- else
+ { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "getter: null",
+ { CanUseGetter: true } => $"getter: static (obj) => (({declaringTypeCompilableName})obj).{clrPropertyName}",
+ { CanUseGetter: false, HasJsonInclude: true }
+ => @$"getter: static (obj) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")",
+ _ => "getter: null"
+ };
+
+ string setterNamedArg = memberMetadata switch
{
- setterNamedArg = "setter: null";
- }
+ { DefaultIgnoreCondition: JsonIgnoreCondition.Always } => "setter: null",
+ { CanUseSetter: true, IsInitOnlySetter: true }
+ => @$"setter: static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{ExceptionMessages.InitOnlyPropertyDeserializationNotSupported}"")",
+ { CanUseSetter: true } when typeGenerationSpec.IsValueType
+ => $@"setter: static (obj, value) => {UnsafeTypeRef}.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value!",
+ { CanUseSetter: true }
+ => @$"setter: static (obj, value) => (({declaringTypeCompilableName})obj).{clrPropertyName} = value!",
+ { CanUseSetter: false, HasJsonInclude: true }
+ => @$"setter: static (obj, value) => throw new {InvalidOperationExceptionTypeRef}(""{string.Format(ExceptionMessages.InaccessibleJsonIncludePropertiesNotSupported, typeGenerationSpec.Type.Name, memberMetadata.ClrName)}"")",
+ _ => "setter: null",
+ };
JsonIgnoreCondition? ignoreCondition = memberMetadata.DefaultIgnoreCondition;
string ignoreConditionNamedArg = ignoreCondition.HasValue
out Dictionary<string, PropertyGenerationSpec>? serializableProperties,
out bool castingRequiredForProps))
{
- string exceptionMessage = @$"""Invalid serializable-property configuration specified for type '{typeRef}'. For more information, see 'JsonSourceGenerationMode.Serialization'.""";
+ string exceptionMessage = string.Format(ExceptionMessages.InvalidSerializablePropertyConfiguration, typeRef);
return GenerateFastPathFuncForType(
serializeMethodName,
typeRef,
- $@"throw new {InvalidOperationExceptionTypeRef}({exceptionMessage});",
+ $@"throw new {InvalidOperationExceptionTypeRef}(""{exceptionMessage}"");",
canBeNull: false); // Skip null check since we want to throw an exception straightaway.
}
converter = factory.CreateConverter(type, {OptionsInstanceVariableName});
if (converter == null || converter is {JsonConverterFactoryTypeRef})
{{
- throw new {InvalidOperationExceptionTypeRef}($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
+ throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.InvalidJsonConverterFactoryOutput}"", factory.GetType()));
}}
}}
{JsonConverterTypeRef}? converter = factory.CreateConverter(type, {Emitter.OptionsInstanceVariableName});
if (converter == null || converter is {JsonConverterFactoryTypeRef})
{{
- throw new {InvalidOperationExceptionTypeRef}($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
+ throw new {InvalidOperationExceptionTypeRef}(string.Format(""{ExceptionMessages.InvalidJsonConverterFactoryOutput}"", factory.GetType()));
}}
return converter;
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
+ private static DiagnosticDescriptor InitOnlyPropertyDeserializationNotSupported { get; } = new DiagnosticDescriptor(
+ id: "SYSLIB1037",
+ title: new LocalizableResourceString(nameof(SR.InitOnlyPropertyDeserializationNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ messageFormat: new LocalizableResourceString(nameof(SR.InitOnlyPropertyDeserializationNotSupportedFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private static DiagnosticDescriptor InaccessibleJsonIncludePropertiesNotSupported { get; } = new DiagnosticDescriptor(
+ id: "SYSLIB1038",
+ title: new LocalizableResourceString(nameof(SR.InaccessibleJsonIncludePropertiesNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ messageFormat: new LocalizableResourceString(nameof(SR.InaccessibleJsonIncludePropertiesNotSupportedFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ category: JsonConstants.SystemTextJsonSourceGenerationName,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
public Parser(Compilation compilation, in JsonSourceGenerationContext sourceGenerationContext)
{
_compilation = compilation;
string? converterInstatiationLogic = null;
bool implementsIJsonOnSerialized = false;
bool implementsIJsonOnSerializing = false;
+ bool hasEncounteredInitOnlyProperties = false;
bool hasTypeFactoryConverter = false;
bool hasPropertyFactoryConverters = false;
dataExtensionPropGenSpec = GetOrAddTypeGenerationSpec(propType, generationMode);
_implicitlyRegisteredTypes.Add(dataExtensionPropGenSpec);
}
+
+ if (!hasEncounteredInitOnlyProperties && spec.CanUseSetter && spec.IsInitOnlySetter)
+ {
+ _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(InitOnlyPropertyDeserializationNotSupported, Location.None, new string[] { type.Name }));
+ hasEncounteredInitOnlyProperties = true;
+ }
+
+ if (spec.HasJsonInclude && (!spec.CanUseGetter || !spec.CanUseSetter || !spec.IsPublic))
+ {
+ _sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(InaccessibleJsonIncludePropertiesNotSupported, Location.None, new string[] { type.Name, spec.ClrName }));
+ }
}
}
out bool canUseGetter,
out bool canUseSetter,
out bool getterIsVirtual,
- out bool setterIsVirtual);
+ out bool setterIsVirtual,
+ out bool setterIsInitOnly);
string clrName = memberInfo.Name;
string runtimePropertyName = DetermineRuntimePropName(clrName, jsonPropertyName, _currentContextNamingPolicy);
RuntimePropertyName = runtimePropertyName,
PropertyNameVarName = propertyNameVarName,
IsReadOnly = isReadOnly,
+ IsInitOnlySetter = setterIsInitOnly,
CanUseGetter = canUseGetter,
CanUseSetter = canUseSetter,
GetterIsVirtual = getterIsVirtual,
out bool canUseGetter,
out bool canUseSetter,
out bool getterIsVirtual,
- out bool setterIsVirtual)
+ out bool setterIsVirtual,
+ out bool setterIsInitOnly)
{
isPublic = false;
canUseGetter = false;
canUseSetter = false;
getterIsVirtual = false;
setterIsVirtual = false;
+ setterIsInitOnly = false;
switch (memberInfo)
{
if (setMethod != null)
{
isReadOnly = false;
+ setterIsInitOnly = setMethod.IsInitOnly();
if (setMethod.IsPublic)
{
isPublic = true;
- canUseSetter = !setMethod.IsInitOnly();
+ canUseSetter = true;
}
else if (setMethod.IsAssembly)
{
- canUseSetter = hasJsonInclude && !setMethod.IsInitOnly();
+ canUseSetter = hasJsonInclude;
}
setterIsVirtual = setMethod.IsVirtual;
/// </summary>
public bool IsProperty { get; init; }
+ /// <summary>
+ /// If representing a property, returns true if either the getter or setter are public.
+ /// </summary>
public bool IsPublic { get; init; }
public bool IsVirtual { get; init; }
public bool IsReadOnly { get; init; }
/// <summary>
+ /// Whether the property has an init-only set method.
+ /// </summary>
+ public bool IsInitOnlySetter { get; init; }
+
+ /// <summary>
/// Whether the property has a public or internal (only usable when JsonIncludeAttribute is specified)
/// getter that can be referenced in generated source code.
/// </summary>
<data name="DataExtensionPropertyInvalidTitle" xml:space="preserve">
<value>Data extension property type invalid.</value>
</data>
-</root>
\ No newline at end of file
+ <data name="InitOnlyPropertyDeserializationNotSupportedTitle" xml:space="preserve">
+ <value>Deserialization of init-only properties is currently not supported in source generation mode.</value>
+ </data>
+ <data name="InitOnlyPropertyDeserializationNotSupportedFormat" xml:space="preserve">
+ <value>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</value>
+ </data>
+ <data name="InaccessibleJsonIncludePropertiesNotSupportedTitle" xml:space="preserve">
+ <value>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</value>
+ </data>
+ <data name="InaccessibleJsonIncludePropertiesNotSupportedFormat" xml:space="preserve">
+ <value>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</value>
+ </data>
+</root>
<target state="translated">Duplicitní název typu</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">Typ {0} má více konstruktorů anotovaných s JsonConstructorAttribute. </target>
<target state="translated">Doppelter Typname</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">Typ "{0}" weist mehrere Konstruktoren mit dem Kommentar "JsonConstructorAttribute" auf.</target>
<target state="translated">Nombre de tipo duplicado.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">El tipo '{0}' tiene varios constructores anotados con 'JsonConstructorAttribute'.</target>
<target state="translated">Nom de type dupliqué.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">Le type' {0} 'a plusieurs constructeurs annotés avec’JsonConstructorAttribute'.</target>
<target state="translated">Nome di tipo duplicato.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">Il tipo '{0}' contiene più costruttori che presentano l'annotazione 'JsonConstructorAttribute'.</target>
<target state="translated">重複した種類名。</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">型 '{0}' には、'JsonConstructorAttribute' で注釈が付けられた複数のコンストラクターがあります。</target>
<target state="translated">중복된 형식 이름입니다.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">'{0}' 형식에 'JsonConstructorAttribute'로 주석이 추가된 여러 생성자가 있습니다.</target>
<target state="translated">Zduplikowana nazwa typu.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">Typ "{0}" ma wiele konstruktorów z adnotacją "JsonConstructorAttribute".</target>
<target state="translated">Nome de tipo duplicado.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">O tipo '{0}' tem vários construtores anotados com 'JsonConstructorAttribute'.</target>
<target state="translated">Дублирующееся имя типа.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">Тип "{0}" имеет несколько конструкторов, аннотированных с использованием JsonConstructorAttribute.</target>
<target state="translated">Yinelenen tür adı.</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">'{0}' türünün 'JsonConstructorAttribute' ile açıklanan birden çok oluşturucusu var.</target>
<target state="translated">重复的类型名称。</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">类型“{0}”具有用 “JsonConstructorAttribute” 批注的多个构造函数。</target>
<target state="translated">重複類型名稱。</target>
<note />
</trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedFormat">
+ <source>The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</source>
+ <target state="new">The member '{0}.{1}' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InaccessibleJsonIncludePropertiesNotSupportedTitle">
+ <source>Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</source>
+ <target state="new">Inaccessible properties annotated with the JsonIncludeAttribute are not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedFormat">
+ <source>The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</source>
+ <target state="new">The type '{0}' defines init-only properties, deserialization of which is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
+ <trans-unit id="InitOnlyPropertyDeserializationNotSupportedTitle">
+ <source>Deserialization of init-only properties is currently not supported in source generation mode.</source>
+ <target state="new">Deserialization of init-only properties is currently not supported in source generation mode.</target>
+ <note />
+ </trans-unit>
<trans-unit id="MultipleJsonConstructorAttributeFormat">
<source>Type '{0}' has multiple constructors annotated with 'JsonConstructorAttribute'.</source>
<target state="translated">類型 '{0}' 包含多個以 'JsonConstructorAttribute' 註解的建構函式。</target>
-<Project>
+<Project>
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AssemblyName>$(MSBuildThisFileName)</AssemblyName>
<Compile Include="CollectionType.cs" />
<Compile Include="JsonConstants.cs" />
<Compile Include="JsonSourceGenerator.Emitter.cs" />
+ <Compile Include="JsonSourceGenerator.Emitter.ExceptionMessages.cs" />
<Compile Include="JsonSourceGenerator.Parser.cs" />
<Compile Include="ObjectConstructionStrategy.cs" />
<Compile Include="ParameterGenerationSpec.cs" />
/// </summary>
public string? RuntimeTypeRef { get; private set; }
- public TypeGenerationSpec? ExtensionDataPropertyTypeSpec { get; private set; }
+ public TypeGenerationSpec? ExtensionDataPropertyTypeSpec { get; private set; }
public string? ConverterInstantiationLogic { get; private set; }
for (int i = 0; i < PropertyGenSpecList.Count; i++)
{
PropertyGenerationSpec propGenSpec = PropertyGenSpecList[i];
- bool hasJsonInclude = propGenSpec.HasJsonInclude;
JsonIgnoreCondition? ignoreCondition = propGenSpec.DefaultIgnoreCondition;
if (ignoreCondition == JsonIgnoreCondition.WhenWritingNull && !propGenSpec.TypeGenerationSpec.CanBeNull)
goto ReturnFalse;
}
- if (!propGenSpec.IsPublic)
+ // In case of JsonInclude fail if either:
+ // 1. the getter is not accessible by the source generator or
+ // 2. neither getter or setter methods are public.
+ if (propGenSpec.HasJsonInclude && (!propGenSpec.CanUseGetter || !propGenSpec.IsPublic))
{
- if (hasJsonInclude)
- {
- goto ReturnFalse;
- }
+ goto ReturnFalse;
+ }
+ // Discard any getters not accessible by the source generator.
+ if (!propGenSpec.CanUseGetter)
+ {
continue;
}
- if (!propGenSpec.IsProperty && !hasJsonInclude && !options.IncludeFields)
+ if (!propGenSpec.IsProperty && !propGenSpec.HasJsonInclude && !options.IncludeFields)
{
continue;
}
castingRequiredForProps = PropertyGenSpecList.Count > serializableProperties.Count;
return true;
-ReturnFalse:
+ ReturnFalse:
serializableProperties = null;
castingRequiredForProps = false;
return false;
}
[Fact]
- public virtual async Task HonorJsonPropertyName()
+ public virtual async Task HonorJsonPropertyName_PrivateGetter()
{
- string json = @"{""prop1"":1,""prop2"":2}";
+ string json = @"{""prop1"":1}";
- var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName>(json);
- Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum);
- Assert.Equal(2, obj.MyInt);
+ var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateGetter>(json);
+ Assert.Equal(MySmallEnum.AnotherValue, obj.GetProxy());
json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
Assert.Contains(@"""prop1"":1", json);
+ }
+
+ [Fact]
+ public virtual async Task HonorJsonPropertyName_PrivateSetter()
+ {
+ string json = @"{""prop2"":2}";
+
+ var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateSetter>(json);
+ Assert.Equal(2, obj.MyInt);
+
+ json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
Assert.Contains(@"""prop2"":2", json);
}
- public struct StructWithPropertiesWithJsonPropertyName
+ public struct StructWithPropertiesWithJsonPropertyName_PrivateGetter
{
[JsonInclude]
[JsonPropertyName("prop1")]
public MySmallEnum MyEnum { private get; set; }
+ // For test validation.
+ internal MySmallEnum GetProxy() => MyEnum;
+ }
+
+ public struct StructWithPropertiesWithJsonPropertyName_PrivateSetter
+ {
[JsonInclude]
[JsonPropertyName("prop2")]
public int MyInt { get; private set; }
- // For test validation.
- internal MySmallEnum GetMyEnum => MyEnum;
+ internal void SetProxy(int myInt) => MyInt = myInt;
}
[Fact]
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Tests;
using System.Threading.Tasks;
""MyUri"":""https://microsoft.com""
}";
- var obj = await JsonSerializerWrapperForString.DeserializeWrapper<MyClass_WithNonPublicAccessors_WithPropertyAttributes>(json);
- Assert.Equal(0, obj.MyInt); // Source gen can't use private setter
- Assert.Equal("Hello", obj.MyString);
- Assert.Equal(2f, obj.GetMyFloat);
- Assert.Equal(new Uri("https://microsoft.com"), obj.MyUri);
-
- json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
- Assert.Contains(@"""MyInt"":0", json);
- Assert.Contains(@"""MyString"":""Hello""", json);
- Assert.DoesNotContain(@"""MyFloat"":", json); // Source gen can't use private setter
- Assert.Contains(@"""MyUri"":""https://microsoft.com""", json);
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<MyClass_WithNonPublicAccessors_WithPropertyAttributes>(json));
+
+ var obj = new MyClass_WithNonPublicAccessors_WithPropertyAttributes();
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj));
}
[Theory]
[InlineData(typeof(StructWithInitOnlyProperty))]
public override async Task InitOnlyProperties(Type type)
{
- // Init-only setters cannot be referenced as get/set helpers in generated code.
- object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type);
- Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj));
+ PropertyInfo property = type.GetProperty("MyInt");
// Init-only properties can be serialized.
- Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+ object obj = Activator.CreateInstance(type);
+ property.SetValue(obj, 1);
+ Assert.Equal(@"{""MyInt"":1}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+
+ // Deserializing init-only properties is not supported.
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type));
}
[Theory]
[InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))]
public override async Task NonPublicInitOnlySetter_With_JsonInclude(Type type)
{
- // Init-only setters cannot be referenced as get/set helpers in generated code.
- object obj = await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type);
- Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj));
+ PropertyInfo property = type.GetProperty("MyInt");
// Init-only properties can be serialized.
- Assert.Equal(@"{""MyInt"":0}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+ object obj = Activator.CreateInstance(type);
+ property.SetValue(obj, 1);
+ Assert.Equal(@"{""MyInt"":1}", await JsonSerializerWrapperForString.SerializeWrapper(obj, type));
+
+ // Deserializing init-only properties is not supported.
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper(@"{""MyInt"":1}", type));
}
[Fact]
string json = @"{""MyEnum"":""AnotherValue"",""MyInt"":2}";
- // Deserialization baseline, without enum converter, we get JsonException.
+ // Deserialization baseline, without enum converter, we get JsonException. NB order of members in deserialized type is significant for this assertion to succeed.
await Assert.ThrowsAsync<JsonException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithConverter>(json));
- var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithConverter>(json, options);
- Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum);
- Assert.Equal(0, obj.MyInt); // Private setter can't be used with source-gen.
+ // JsonInclude not supported in source gen.
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithConverter>(json, options));
- // ConverterForInt32 throws this exception.
- await Assert.ThrowsAsync<NotImplementedException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options));
+ // JsonInclude on private getters not supported.
+ var obj = new StructWithPropertiesWithConverter();
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj, options));
}
[Fact]
Assert.Equal(3, obj.Y);
Assert.Equal(4, obj.GetZ);
- json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
- Assert.Contains(@"""W"":1", json);
- Assert.Contains(@"""X"":2", json);
- Assert.Contains(@"""Y"":3", json);
- Assert.DoesNotContain(@"""Z"":", json); // Private setter cannot be used with source gen.
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.SerializeWrapper(obj));
+ }
+
+ [Fact]
+ public override async Task HonorJsonPropertyName_PrivateGetter()
+ {
+ string json = @"{""prop1"":1}";
+
+ var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateGetter>(json);
+ Assert.Equal(MySmallEnum.AnotherValue, obj.GetProxy());
+
+ // JsonInclude for private members not supported in source gen
+ await Assert.ThrowsAsync<InvalidOperationException>(async() => await JsonSerializerWrapperForString.SerializeWrapper(obj));
}
[Fact]
- public override async Task HonorJsonPropertyName()
+ public override async Task HonorJsonPropertyName_PrivateSetter()
{
- string json = @"{""prop1"":1,""prop2"":2}";
+ string json = @"{""prop2"":2}";
- var obj = await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName>(json);
- Assert.Equal(MySmallEnum.AnotherValue, obj.GetMyEnum);
- Assert.Equal(0, obj.MyInt); // Private setter cannot be used with source gen.
+ // JsonInclude for private members not supported in source gen
+ await Assert.ThrowsAsync<InvalidOperationException>(async () => await JsonSerializerWrapperForString.DeserializeWrapper<StructWithPropertiesWithJsonPropertyName_PrivateSetter>(json));
- json = await JsonSerializerWrapperForString.SerializeWrapper(obj);
- Assert.DoesNotContain(@"""prop1"":", json); // Private getter cannot be used with source gen.
- Assert.Contains(@"""prop2"":0", json);
+ var obj = new StructWithPropertiesWithJsonPropertyName_PrivateSetter();
+ obj.SetProxy(2);
+ Assert.Equal(json, await JsonSerializerWrapperForString.SerializeWrapper(obj));
}
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows))]
[JsonSerializable(typeof(MyClass_WithNonPublicAccessors))]
[JsonSerializable(typeof(ClassWithThingsToIgnore_PerProperty))]
- [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName))]
+ [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateGetter))]
+ [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateSetter))]
[JsonSerializable(typeof(ClassWithValueAndReferenceTypes))]
[JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))]
internal sealed partial class PropertyVisibilityTestsContext_Metadata : JsonSerializerContext
[JsonSerializable(typeof(ClassTwiceInheritedWithPropertyPolicyConflictWhichThrows))]
[JsonSerializable(typeof(MyClass_WithNonPublicAccessors))]
[JsonSerializable(typeof(ClassWithThingsToIgnore_PerProperty))]
- [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName))]
+ [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateGetter))]
+ [JsonSerializable(typeof(StructWithPropertiesWithJsonPropertyName_PrivateSetter))]
[JsonSerializable(typeof(ClassWithValueAndReferenceTypes))]
[JsonSerializable(typeof(ClassWithReadOnlyStringProperty_IgnoreWhenWritingDefault))]
internal sealed partial class PropertyVisibilityTestsContext_Default : JsonSerializerContext
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<!-- SYSLIB0020: JsonSerializerOptions.IgnoreNullValues is obsolete -->
- <NoWarn>$(NoWarn);SYSLIB0020</NoWarn>
+ <!-- SYSLIB1037: Suppress init-only property deserialization warning -->
+ <!-- SYSLIB1038: Suppress JsonInclude on inaccessible members warning -->
+ <NoWarn>$(NoWarn);SYSLIB0020;SYSLIB1037;SYSLIB1038</NoWarn>
</PropertyGroup>
<PropertyGroup>
return CreateCompilation(source);
}
+ public static Compilation CreateCompilationWithInitOnlyProperties()
+ {
+ string source = @"
+ using System;
+ using System.Text.Json.Serialization;
+
+ namespace HelloWorld
+ {
+ public class Location
+ {
+ public int Id { get; init; }
+ public string Address1 { get; init; }
+ public string Address2 { get; init; }
+ public string City { get; init; }
+ public string State { get; init; }
+ public string PostalCode { get; init; }
+ public string Name { get; init; }
+ public string PhoneNumber { get; init; }
+ public string Country { get; init; }
+ }
+
+ [JsonSerializable(typeof(Location))]
+ public partial class MyJsonContext : JsonSerializerContext
+ {
+ }
+ }";
+
+ return CreateCompilation(source);
+ }
+
+ public static Compilation CreateCompilationWithInaccessibleJsonIncludeProperties()
+ {
+ string source = @"
+ using System;
+ using System.Text.Json.Serialization;
+
+ namespace HelloWorld
+ {
+ public class Location
+ {
+ [JsonInclude]
+ public int Id { get; private set; }
+ [JsonInclude]
+ public string Address1 { get; internal set; }
+ [JsonInclude]
+ private string Address2 { get; set; }
+ [JsonInclude]
+ public string PhoneNumber { internal get; set; }
+ [JsonInclude]
+ public string Country { private get; set; }
+ }
+
+ [JsonSerializable(typeof(Location))]
+ public partial class MyJsonContext : JsonSerializerContext
+ {
+ }
+ }";
+
+ return CreateCompilation(source);
+ }
+
internal static void CheckDiagnosticMessages(ImmutableArray<Diagnostic> diagnostics, DiagnosticSeverity level, string[] expectedMessages)
{
string[] actualMessages = diagnostics.Where(diagnostic => diagnostic.Severity == level).Select(diagnostic => diagnostic.GetMessage()).ToArray();
- // Can't depending on reflection order when generating type metadata.
+ // Can't depend on reflection order when generating type metadata.
Array.Sort(actualMessages);
Array.Sort(expectedMessages);
CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, Array.Empty<string>());
CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Error, Array.Empty<string>());
}
+
+ [Fact]
+ public void WarnOnClassesWithInitOnlyProperties()
+ {
+ Compilation compilation = CompilationHelper.CreateCompilationWithInitOnlyProperties();
+ JsonSourceGenerator generator = new JsonSourceGenerator();
+ CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator);
+
+ string[] expectedWarningDiagnostics = new string[] { "The type 'Location' defines init-only properties, deserialization of which is currently not supported in source generation mode." };
+
+ CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Info, Array.Empty<string>());
+ CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, expectedWarningDiagnostics);
+ CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Error, Array.Empty<string>());
+ }
+
+ [Fact]
+ public void WarnOnClassesWithInaccessibleJsonIncludeProperties()
+ {
+ Compilation compilation = CompilationHelper.CreateCompilationWithInaccessibleJsonIncludeProperties();
+ JsonSourceGenerator generator = new JsonSourceGenerator();
+ CompilationHelper.RunGenerators(compilation, out var generatorDiags, generator);
+
+ string[] expectedWarningDiagnostics = new string[]
+ {
+ "The member 'Location.Id' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.",
+ "The member 'Location.Address2' has been annotated with the JsonIncludeAttribute but is not visible to the source generator.",
+ "The member 'Location.Country' has been annotated with the JsonIncludeAttribute but is not visible to the source generator."
+ };
+
+ CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Info, Array.Empty<string>());
+ CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, expectedWarningDiagnostics);
+ CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Error, Array.Empty<string>());
+ }
}
}