diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx
index af1136a18e2f24..76086418dd536e 100644
--- a/src/libraries/System.Text.Json/src/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx
@@ -244,7 +244,7 @@
The requested operation requires an element of type '{0}', but the target element has type '{1}'.
- Default TypeInfoResolver and custom TypeInfoResolver after first usage cannot be changed.
+ Default TypeInfoResolver and custom TypeInfoResolver cannot be changed after first usage.
JsonTypeInfo cannot be changed after first usage.
@@ -662,4 +662,7 @@
JsonPropertyInfo with name '{0}' for type '{1}' is already bound to different JsonTypeInfo.
+
+ Using JsonTypeInfo for serialization is not possible when TypeInfoResolver has not been set.
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs
index 63c9afc8207b4c..e624b8db4c1103 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs
@@ -655,6 +655,12 @@ internal void InitializeForReflectionSerializer()
private JsonTypeInfo? GetTypeInfoInternal(Type type)
{
IJsonTypeInfoResolver? resolver = _effectiveJsonTypeInfoResolver ?? _typeInfoResolver;
+
+ if (resolver == null)
+ {
+ ThrowHelper.ThrowInvalidOperationException_JsonTypeInfoUsedButTypeInfoResolverNotSet();
+ }
+
JsonTypeInfo? info = resolver?.GetTypeInfo(type, this);
if (info != null)
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs
index c774aaad5bca75..0f08484bdc69a2 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs
@@ -116,6 +116,12 @@ public static void ThrowInvalidOperationException_ResolverTypeInfoOptionsNotComp
throw new InvalidOperationException(SR.ResolverTypeInfoOptionsNotCompatible);
}
+ [DoesNotReturn]
+ public static void ThrowInvalidOperationException_JsonTypeInfoUsedButTypeInfoResolverNotSet()
+ {
+ throw new InvalidOperationException(SR.JsonTypeInfoUsedButTypeInfoResolverNotSet);
+ }
+
[DoesNotReturn]
public static void ThrowInvalidOperationException_SerializationConverterOnAttributeInvalid(Type classType, MemberInfo? memberInfo)
{
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs
index a1b1b3d2cc0389..97e7f2b6e67cd9 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs
@@ -7,6 +7,7 @@
using System.Text;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
+using Microsoft.DotNet.RemoteExecutor;
using Xunit;
namespace System.Text.Json.Serialization.Tests
@@ -178,6 +179,79 @@ public static void ModifiersAreCalledAndModifyTypeInfos()
Assert.True(secondModifierCalled);
}
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void StaticInitialization_SerializationWithJsonTypeInfoWithoutSettingTypeInfoResolverThrows()
+ {
+ RemoteExecutor.Invoke(static () =>
+ {
+ JsonSerializerOptions o = new();
+ DefaultJsonTypeInfoResolver r = new();
+ // note: TypeInfoResolver not set
+ JsonTypeInfo ti = (JsonTypeInfo)r.GetTypeInfo(typeof(SomeClass), o);
+ SomeClass obj = new()
+ {
+ ObjProp = "test",
+ IntProp = 42,
+ };
+
+ // TODO: reasses if this is expected behavior
+ Assert.Throws(() => JsonSerializer.Serialize(obj, ti));
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void StaticInitialization_DeserializationWithJsonTypeInfoWithoutSettingTypeInfoResolverThrows()
+ {
+ RemoteExecutor.Invoke(static () =>
+ {
+ JsonSerializerOptions o = new();
+ DefaultJsonTypeInfoResolver r = new();
+ // note: TypeInfoResolver not set
+ JsonTypeInfo ti = (JsonTypeInfo)r.GetTypeInfo(typeof(SomeClass), o);
+
+ // TODO: reasses if this is expected behavior
+ string json = """{"ObjProp":"test","IntProp":42}""";
+ Assert.Throws(() => JsonSerializer.Deserialize(json, ti));
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void StaticInitialization_SerializationWithJsonTypeInfoWhenTypeInfoResolverSetIsPossible()
+ {
+ RemoteExecutor.Invoke(static () =>
+ {
+ JsonSerializerOptions o = new();
+ DefaultJsonTypeInfoResolver r = new();
+ o.TypeInfoResolver = r;
+ JsonTypeInfo ti = (JsonTypeInfo)r.GetTypeInfo(typeof(SomeClass), o);
+ SomeClass obj = new()
+ {
+ ObjProp = "test",
+ IntProp = 42,
+ };
+
+ string json = JsonSerializer.Serialize(obj, ti);
+ Assert.Equal("""{"ObjProp":"test","IntProp":42}""", json);
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void StaticInitialization_DeserializationWithJsonTypeInfoWhenTypeInfoResolverSetIsPossible()
+ {
+ RemoteExecutor.Invoke(static () =>
+ {
+ JsonSerializerOptions o = new();
+ DefaultJsonTypeInfoResolver r = new();
+ o.TypeInfoResolver = r;
+ JsonTypeInfo ti = (JsonTypeInfo)r.GetTypeInfo(typeof(SomeClass), o);
+ string json = """{"ObjProp":"test","IntProp":42}""";
+ SomeClass deserialized = JsonSerializer.Deserialize(json, ti);
+ Assert.IsType(deserialized.ObjProp);
+ Assert.Equal("test", ((JsonElement)deserialized.ObjProp).GetString());
+ Assert.Equal(42, deserialized.IntProp);
+ }).Dispose();
+ }
+
private static void InvokeGeneric(Type type, string methodName, params object[] args)
{
typeof(DefaultJsonTypeInfoResolverTests)