Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON contract customization #70435

Merged
merged 10 commits into from
Jun 22, 2022
246 changes: 109 additions & 137 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1473,11 +1473,11 @@ private static bool PropertyAccessorCanBeReferenced(MethodInfo? accessor)

if (forType)
{
return $"{Emitter.GetConverterFromFactoryMethodName}(typeof({type.GetCompilableName()}), new {converterType.GetCompilableName()}())";
return $"{Emitter.GetConverterFromFactoryMethodName}({OptionsLocalVariableName}, typeof({type.GetCompilableName()}), new {converterType.GetCompilableName()}())";
}
else
{
return $"{Emitter.JsonContextVarName}.{Emitter.GetConverterFromFactoryMethodName}<{type.GetCompilableName()}>(new {converterType.GetCompilableName()}())";
return $"{Emitter.GetConverterFromFactoryMethodName}<{type.GetCompilableName()}>({OptionsLocalVariableName}, new {converterType.GetCompilableName()}())";
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ internal sealed class TypeGenerationSpec
/// </summary>
public string TypeInfoPropertyName { get; set; }

/// <summary>
/// Method used to generate JsonTypeInfo given options instance
/// </summary>
public string CreateTypeInfoMethodName => $"Create_{TypeInfoPropertyName}";

public JsonSourceGenerationMode GenerationMode { get; set; }

public bool GenerateMetadata => GenerationModeIsSpecified(JsonSourceGenerationMode.Metadata);
Expand Down
65 changes: 60 additions & 5 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,13 @@ public JsonSerializerOptions(System.Text.Json.JsonSerializerOptions options) { }
public System.Text.Json.JsonCommentHandling ReadCommentHandling { get { throw null; } set { } }
public System.Text.Json.Serialization.ReferenceHandler? ReferenceHandler { get { throw null; } set { } }
public System.Collections.Generic.IList<System.Text.Json.Serialization.JsonPolymorphicTypeConfiguration> PolymorphicTypeConfigurations { get { throw null; } }
public System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver TypeInfoResolver
{
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")]
get { throw null; }
set { }
}
public System.Text.Json.Serialization.JsonUnknownTypeHandling UnknownTypeHandling { get { throw null; } set { } }
public bool WriteIndented { get { throw null; } set { } }
public void AddContext<TContext>() where TContext : System.Text.Json.Serialization.JsonSerializerContext, new() { }
Expand Down Expand Up @@ -950,12 +957,13 @@ public JsonSerializableAttribute(System.Type type) { }
public string? TypeInfoPropertyName { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonSourceGenerationMode GenerationMode { get { throw null; } set { } }
}
public abstract partial class JsonSerializerContext
public abstract partial class JsonSerializerContext : System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver
{
protected JsonSerializerContext(System.Text.Json.JsonSerializerOptions? options) { }
protected abstract System.Text.Json.JsonSerializerOptions? GeneratedSerializerOptions { get; }
public System.Text.Json.JsonSerializerOptions Options { get { throw null; } }
public abstract System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(System.Type type);
System.Text.Json.Serialization.Metadata.JsonTypeInfo System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple = false)]
public sealed partial class JsonSourceGenerationOptionsAttribute : System.Text.Json.Serialization.JsonAttribute
Expand Down Expand Up @@ -1044,6 +1052,20 @@ protected ReferenceResolver() { }
}
namespace System.Text.Json.Serialization.Metadata
{
public class DefaultJsonTypeInfoResolver : System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver
{
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")]
public DefaultJsonTypeInfoResolver() { }

public virtual System.Text.Json.Serialization.Metadata.JsonTypeInfo GetTypeInfo(System.Type type, System.Text.Json.JsonSerializerOptions options) { throw null; }

public System.Collections.Generic.IList<System.Action<System.Text.Json.Serialization.Metadata.JsonTypeInfo>> Modifiers { get; }
}
public interface IJsonTypeInfoResolver
{
System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(System.Type type, System.Text.Json.JsonSerializerOptions options);
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public sealed partial class JsonCollectionInfoValues<TCollection>
{
Expand Down Expand Up @@ -1116,6 +1138,7 @@ public static partial class JsonMetadataServices
public static System.Text.Json.Serialization.JsonConverter<T> GetUnsupportedTypeConverter<T>() { throw null; }
public static System.Text.Json.Serialization.JsonConverter<T> GetEnumConverter<T>(System.Text.Json.JsonSerializerOptions options) where T : struct { throw null; }
public static System.Text.Json.Serialization.JsonConverter<T?> GetNullableConverter<T>(System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> underlyingTypeInfo) where T : struct { throw null; }
public static System.Text.Json.Serialization.JsonConverter<T?> GetNullableConverter<T>(System.Text.Json.JsonSerializerOptions options) where T : struct { throw null; }
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public sealed partial class JsonObjectInfoValues<T>
Expand All @@ -1138,10 +1161,17 @@ public JsonParameterInfoValues() { }
public System.Type ParameterType { get { throw null; } init { } }
public int Position { get { throw null; } init { } }
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public abstract partial class JsonPropertyInfo
{
internal JsonPropertyInfo() { }
public System.Text.Json.Serialization.JsonConverter? CustomConverter { get { throw null; } set { } }
public System.Func<object, object?>? Get { get { throw null; } set { } }
public string Name { get { throw null; } set { } }
public System.Text.Json.Serialization.JsonNumberHandling? NumberHandling { get { throw null; } set { } }
public System.Type PropertyType { get { throw null; } }
public System.Text.Json.JsonSerializerOptions Options { get { throw null; } }
public System.Action<object, object?>? Set { get { throw null; } set { } }
public System.Func<object, object?, bool>? ShouldSerialize { get { throw null; } set { } }
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public sealed partial class JsonPropertyInfoValues<T>
Expand All @@ -1162,15 +1192,40 @@ public JsonPropertyInfoValues() { }
public System.Text.Json.Serialization.Metadata.JsonTypeInfo PropertyTypeInfo { get { throw null; } init { } }
public System.Action<object, T?>? Setter { get { throw null; } init { } }
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public partial class JsonTypeInfo
public static class JsonTypeInfoResolver
{
public static System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver Combine(params System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver[] resolvers) { throw null; }
}
public abstract partial class JsonTypeInfo
{
internal JsonTypeInfo() { }
public System.Text.Json.JsonSerializerOptions Options { get { throw null; } }
public System.Collections.Generic.IList<System.Text.Json.Serialization.Metadata.JsonPropertyInfo> Properties { get { throw null; } }
public System.Type Type { get { throw null; } }
public System.Text.Json.Serialization.JsonConverter Converter { get { throw null; } }
public System.Func<object>? CreateObject { get { throw null; } set { } }
public System.Text.Json.Serialization.Metadata.JsonTypeInfoKind Kind { get { throw null; } }
public System.Text.Json.Serialization.JsonNumberHandling? NumberHandling { get { throw null; } set { } }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")]
public static System.Text.Json.Serialization.Metadata.JsonTypeInfo<T> CreateJsonTypeInfo<T>(System.Text.Json.JsonSerializerOptions options) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")]
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")]
public static System.Text.Json.Serialization.Metadata.JsonTypeInfo CreateJsonTypeInfo(System.Type type, System.Text.Json.JsonSerializerOptions options) { throw null; }
public JsonPropertyInfo CreateJsonPropertyInfo(Type propertyType, string name) { throw null; }
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public abstract partial class JsonTypeInfo<T> : System.Text.Json.Serialization.Metadata.JsonTypeInfo
{
internal JsonTypeInfo() { }
public new System.Func<T>? CreateObject { get { throw null; } set { } }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public System.Action<System.Text.Json.Utf8JsonWriter, T>? SerializeHandler { get { throw null; } }
}
public enum JsonTypeInfoKind
{
None = 0,
Object = 1,
Enumerable = 2,
Dictionary = 3
}
}
87 changes: 57 additions & 30 deletions src/libraries/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

<!--
Microsoft ResX Schema
Version 2.0

The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.

Example:

... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>

There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.

Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.

The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:

Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.

mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.

mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -243,6 +243,15 @@
<data name="JsonElementHasWrongType" xml:space="preserve">
<value>The requested operation requires an element of type '{0}', but the target element has type '{1}'.</value>
</data>
<data name="TypeInfoResolverImmutable" xml:space="preserve">
<value>Default TypeInfoResolver and custom TypeInfoResolver cannot be changed after first usage.</value>
</data>
<data name="TypeInfoImmutable" xml:space="preserve">
<value>JsonTypeInfo cannot be changed after first usage.</value>
</data>
<data name="PropertyInfoImmutable" xml:space="preserve">
<value>JsonPropertyInfo cannot be changed after first usage.</value>
</data>
<data name="MaxDepthMustBePositive" xml:space="preserve">
<value>Max depth must be positive.</value>
</data>
Expand Down Expand Up @@ -390,6 +399,12 @@
<data name="SerializationConverterNotCompatible" xml:space="preserve">
<value>The converter '{0}' is not compatible with the type '{1}'.</value>
</data>
<data name="ResolverTypeNotCompatible" xml:space="preserve">
<value>TypeInfoResolver expected to return JsonTypeInfo of type '{0}' but returned JsonTypeInfo of type '{1}'.</value>
</data>
<data name="ResolverTypeInfoOptionsNotCompatible" xml:space="preserve">
<value>TypeInfoResolver expected to return JsonTypeInfo options bound to the JsonSerializerOptions provided in the argument.</value>
</data>
<data name="SerializationConverterWrite" xml:space="preserve">
<value>The converter '{0}' wrote too much or not enough.</value>
</data>
Expand Down Expand Up @@ -572,13 +587,13 @@
<data name="NoMetadataForType" xml:space="preserve">
<value>Metadata for type '{0}' was not provided to the serializer. The serializer method used does not support reflection-based creation of serialization-related type metadata. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.</value>
</data>
<data name="NodeCollectionIsReadOnly" xml:space="preserve">
<data name="CollectionIsReadOnly" xml:space="preserve">
<value>Collection is read-only.</value>
</data>
<data name="NodeArrayIndexNegative" xml:space="preserve">
<data name="ArrayIndexNegative" xml:space="preserve">
<value>Number was less than 0.</value>
</data>
<data name="NodeArrayTooSmall" xml:space="preserve">
<data name="ArrayTooSmall" xml:space="preserve">
<value>Destination array was not long enough.</value>
</data>
<data name="NodeJsonObjectCustomConverterNotAllowedOnExtensionProperty" xml:space="preserve">
Expand Down Expand Up @@ -638,4 +653,16 @@
<data name="Polymorphism_RuntimeTypeDiamondAmbiguity" xml:space="preserve">
<value>Runtime type '{0}' has a diamond ambiguity between derived types '{1}' and '{2}' of polymorphic type '{3}'. Consider either removing one of the derived types or removing the 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' setting.</value>
</data>
<data name="JsonTypeInfoOperationNotPossibleForKindNone" xml:space="preserve">
<value>Operation is not possible when Kind is JsonTypeKind.None.</value>
</data>
<data name="CombineOneOfResolversIsNull" xml:space="preserve">
<value>One of the provided resolvers is null.</value>
</data>
<data name="JsonPropertyInfoBoundToDifferentParent" xml:space="preserve">
<value>JsonPropertyInfo with name '{0}' for type '{1}' is already bound to different JsonTypeInfo.</value>
</data>
<data name="JsonTypeInfoUsedButTypeInfoResolverNotSet" xml:space="preserve">
<value>JsonTypeInfo metadata references a JsonSerializerOptions instance that doesn't specify a TypeInfoResolver.</value>
</data>
</root>
Loading