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 2d3b2af2e99d66..6447ea8338f660 100644
--- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj
+++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
@@ -126,6 +126,8 @@ The System.Text.Json library is built-in as part of the shared framework in .NET
+
+
diff --git a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
index f2e9945b49c477..406cc0074726d6 100644
--- a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
+++ b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
@@ -6,12 +6,14 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
+using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace System.Text.Json.Reflection
{
internal static partial class ReflectionExtensions
{
+ private static readonly Type s_jsonValuePrimitiveType = typeof(JsonValuePrimitive<>);
private static readonly Type s_nullableType = typeof(Nullable<>);
///
@@ -164,5 +166,12 @@ public static MemberInfo GetGenericMemberDefinition(this MemberInfo member)
return member;
}
+
+ ///
+ /// Returns when the given type is of type .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsJsonValuePrimitiveOfT(this Type type) =>
+ type.IsGenericType && type.GetGenericTypeDefinition() == s_jsonValuePrimitiveType;
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonValuePrimitiveConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonValuePrimitiveConverter.cs
new file mode 100644
index 00000000000000..56db9557212b46
--- /dev/null
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonValuePrimitiveConverter.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json.Nodes;
+using System.Text.Json.Schema;
+
+namespace System.Text.Json.Serialization.Converters.Node
+{
+ internal sealed class JsonValuePrimitiveConverter : JsonConverter?>
+ {
+ private readonly JsonConverter _elementConverter;
+
+ public override bool HandleNull => true;
+ internal override bool CanPopulate => _elementConverter.CanPopulate;
+ internal override bool ConstructorIsParameterized => _elementConverter.ConstructorIsParameterized;
+ internal override Type? ElementType => typeof(JsonValuePrimitive);
+ internal override JsonConverter? NullableElementConverter => _elementConverter;
+
+ public JsonValuePrimitiveConverter(JsonConverter elementConverter)
+ {
+ _elementConverter = elementConverter;
+ }
+
+ public override JsonValuePrimitive? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.Null)
+ {
+ return null;
+ }
+
+ T value = _elementConverter.Read(ref reader, typeof(T), options)!;
+ JsonValuePrimitive returnValue = new JsonValuePrimitive(value, _elementConverter, null);
+
+ return returnValue;
+ }
+
+ public override void Write(Utf8JsonWriter writer, JsonValuePrimitive? value, JsonSerializerOptions options)
+ {
+ if (value is null)
+ {
+ writer.WriteNullValue();
+ return;
+ }
+
+ value.WriteTo(writer, options);
+ }
+
+ internal override JsonSchema? GetSchema(JsonNumberHandling numberHandling) => _elementConverter.GetSchema(numberHandling);
+ }
+}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonValuePrimitiveFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonValuePrimitiveFactory.cs
new file mode 100644
index 00000000000000..644e13dd59b08c
--- /dev/null
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonValuePrimitiveFactory.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Text.Json.Reflection;
+using System.Text.Json.Serialization.Converters.Node;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
+ internal sealed class JsonValuePrimitiveFactory : JsonConverterFactory
+ {
+ public override bool CanConvert(Type typeToConvert)
+ {
+ return typeToConvert.IsJsonValuePrimitiveOfT();
+ }
+
+ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
+ {
+ Debug.Assert(typeToConvert.IsJsonValuePrimitiveOfT());
+
+ Type valueTypeToConvert = typeToConvert.GetGenericArguments()[0];
+ JsonConverter valueConverter = options.GetConverterInternal(valueTypeToConvert);
+
+ return CreateValueConverter(valueTypeToConvert, valueConverter);
+ }
+
+ public static JsonConverter CreateValueConverter(Type valueTypeToConvert, JsonConverter valueConverter)
+ {
+ return (JsonConverter)Activator.CreateInstance(
+ GetJsonValuePrimitiveConverterType(valueTypeToConvert),
+ BindingFlags.Instance | BindingFlags.Public,
+ binder: null,
+ args: new object[] { valueConverter },
+ culture: null)!;
+ }
+
+ [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+ private static Type GetJsonValuePrimitiveConverterType(Type valueTypeToConvert) => typeof(JsonValuePrimitiveConverter<>).MakeGenericType(valueTypeToConvert);
+ }
+}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Converters.cs
index c96e2a1c8f5da5..bce3936da6cd51 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Converters.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Converters.cs
@@ -26,6 +26,7 @@ private static JsonConverterFactory[] GetDefaultFactoryConverters()
// Nullable converter should always be next since it forwards to any nullable type.
new NullableConverterFactory(),
new EnumConverterFactory(),
+ new JsonValuePrimitiveFactory(),
new JsonNodeConverterFactory(),
new FSharpTypeConverterFactory(),
new MemoryConverterFactory(),
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs
index 29ae9662eedd4b..c5621345ba63c9 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs
@@ -12,6 +12,115 @@ namespace System.Text.Json.Nodes.Tests
{
public static class JsonValueTests
{
+ public static IEnumerable