public static void CreateObjectForDictionaryWithDefaults(bool useTypedCreateObject) { DefaultJsonTypeInfoResolver resolver = new(); resolver.Modifiers.Add((ti) => { if (ti.Type == typeof(Dictionary <string, int>)) { Func <Dictionary <string, int> > createObj = () => new Dictionary <string, int> { ["*test*"] = -1 }; if (useTypedCreateObject) { JsonTypeInfo <Dictionary <string, int> > typedTi = ti as JsonTypeInfo <Dictionary <string, int> >; Assert.NotNull(typedTi); typedTi.CreateObject = createObj; } else { // we want to make sure Func is not a cast to the untyped one ti.CreateObject = () => createObj(); } } }); JsonSerializerOptions options = new JsonSerializerOptions(); options.IncludeFields = true; options.TypeInfoResolver = resolver; TestClassWithDictionaries originalObj = new() { DictionaryProperty1 = new Dictionary <string, int> { ["test1"] = 2, ["test2"] = 3 }, DictionaryProperty2 = new Dictionary <string, int>(), }; string json = JsonSerializer.Serialize(originalObj, options); Assert.Equal("" "{" DictionaryProperty1 ":{" test1 ":2," test2 ":3}," DictionaryProperty2 ":{}}" "", json); TestClassWithDictionaries deserialized = JsonSerializer.Deserialize <TestClassWithDictionaries>(json, options); Assert.Equal(new Dictionary <string, int> { ["*test*"] = -1, ["test1"] = 2, ["test2"] = 3 }, deserialized.DictionaryProperty1); Assert.Equal(new Dictionary <string, int> { ["*test*"] = -1 }, deserialized.DictionaryProperty2); json = @"{}"; deserialized = JsonSerializer.Deserialize <TestClassWithDictionaries>(json, options); Assert.Null(deserialized.DictionaryProperty1); Assert.Null(deserialized.DictionaryProperty2); json = "" "{" DictionaryProperty2 ":{" foo ":123}}" ""; deserialized = JsonSerializer.Deserialize <TestClassWithDictionaries>(json, options); Assert.Null(deserialized.DictionaryProperty1); Assert.Equal(new Dictionary <string, int> { ["*test*"] = -1, ["foo"] = 123 }, deserialized.DictionaryProperty2); }
public override Task SerializeWrapper <T>(Stream stream, T value, JsonTypeInfo <T> jsonTypeInfo) { JsonSerializer.Serialize(stream, value, jsonTypeInfo); return(Task.CompletedTask); }
public override Task <T> DeserializeWrapper <T>(string json, JsonTypeInfo <T> jsonTypeInfo) { Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json), OptionsHelpers.GetReaderOptions(jsonTypeInfo?.Options)); return(Task.FromResult(JsonSerializer.Deserialize(ref reader, jsonTypeInfo))); }
public override Task <string> SerializeWrapper <T>(T value, JsonTypeInfo <T> jsonTypeInfo) { return(Task.FromResult(JsonSerializer.Serialize(value, jsonTypeInfo))); }
public override Task SerializeWrapper <T>(Stream stream, T value, JsonTypeInfo <T> jsonTypeInfo) { return(JsonSerializer.SerializeAsync(stream, value, jsonTypeInfo)); }
/// <summary> /// Parses the text representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="json">JSON text to parse.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="json"/> is <see langword="null"/>. /// /// -or- /// /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// The JSON is invalid. /// /// -or- /// /// <typeparamref name="TValue" /> is not compatible with the JSON. /// /// -or- /// /// There is remaining data in the string beyond a single JSON value.</exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <remarks>Using a <see cref="string"/> is not as efficient as using the /// UTF-8 methods since the implementation natively uses UTF-8. /// </remarks> public static TValue?Deserialize <TValue>([StringSyntax(StringSyntaxAttribute.Json)] ReadOnlySpan <char> json, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo is null) { ThrowHelper.ThrowArgumentNullException(nameof(jsonTypeInfo)); } return(ReadFromSpan <TValue?>(json, jsonTypeInfo)); }
public static TValue?Deserialize <TValue>(this JsonElement element, JsonSerializerOptions?options = null) { JsonTypeInfo jsonTypeInfo = GetTypeInfo(options, typeof(TValue)); return(ReadUsingMetadata <TValue>(element, jsonTypeInfo)); }
public static JsonDocument SerializeToDocument<TValue>(TValue value, JsonSerializerOptions? options = null) { Type runtimeType = GetRuntimeType(value); JsonTypeInfo jsonTypeInfo = GetTypeInfo(options, runtimeType); return WriteDocumentUsingSerializer(value, jsonTypeInfo); }
public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerOptions? options = null) { Type runtimeType = GetRuntimeTypeAndValidateInputType(value, inputType); JsonTypeInfo jsonTypeInfo = GetTypeInfo(options, runtimeType); return WriteDocumentUsingSerializer(value, jsonTypeInfo); }
public static Task<TValue?> GetFromJsonAsync<TValue>(this HttpClient client, Uri? requestUri, JsonTypeInfo<TValue> jsonTypeInfo, CancellationToken cancellationToken = default) { if (client is null) { throw new ArgumentNullException(nameof(client)); } Task<HttpResponseMessage> taskResponse = client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken); return GetFromJsonAsyncCore<TValue>(taskResponse, jsonTypeInfo, cancellationToken); }
/// <summary> /// Same as GetConverter but without defaulting to reflection converters. /// </summary> internal JsonConverter GetConverterInternal(Type typeToConvert) { JsonTypeInfo jsonTypeInfo = GetTypeInfoInternal(typeToConvert, ensureConfigured: false, resolveIfMutable: true); return(jsonTypeInfo.Converter); }
/// <summary> /// Parses the text representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <typeparam name="TValue">The type to deserialize the JSON value into.</typeparam> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="json">JSON text to parse.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="json"/> is <see langword="null"/>. /// /// -or- /// /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// The JSON is invalid. /// /// -or- /// /// <typeparamref name="TValue" /> is not compatible with the JSON. /// /// -or- /// /// There is remaining data in the string beyond a single JSON value.</exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <remarks>Using a <see cref="string"/> is not as efficient as using the /// UTF-8 methods since the implementation natively uses UTF-8. /// </remarks> public static TValue?Deserialize <TValue>([StringSyntax(StringSyntaxAttribute.Json)] ReadOnlySpan <char> json, JsonTypeInfo <TValue> jsonTypeInfo) { // default/null span is treated as empty if (jsonTypeInfo == null) { throw new ArgumentNullException(nameof(jsonTypeInfo)); } return(ReadFromSpan <TValue?>(json, jsonTypeInfo)); }
static void CreatePropertyAndCheckOptions(JsonSerializerOptions expectedOptions, JsonTypeInfo typeInfo) { JsonPropertyInfo propertyInfo = typeInfo.CreateJsonPropertyInfo(typeof(string), "test"); Assert.Same(expectedOptions, propertyInfo.Options); }
public static void CombineCustomResolverWithDefault() { TestResolver resolver = new TestResolver((Type type, JsonSerializerOptions options) => { if (type != typeof(TestClass)) { return(null); } JsonTypeInfo <TestClass> ti = JsonTypeInfo.CreateJsonTypeInfo <TestClass>(options); ti.CreateObject = () => new TestClass() { TestField = string.Empty, TestProperty = 42, }; JsonPropertyInfo field = ti.CreateJsonPropertyInfo(typeof(string), "MyTestField"); field.Get = (o) => { TestClass obj = (TestClass)o; return(obj.TestField ?? string.Empty); }; field.Set = (o, val) => { TestClass obj = (TestClass)o; string value = (string?)val ?? string.Empty; obj.TestField = value; }; field.ShouldSerialize = (o, val) => (string)val != string.Empty; JsonPropertyInfo prop = ti.CreateJsonPropertyInfo(typeof(int), "MyTestProperty"); prop.Get = (o) => { TestClass obj = (TestClass)o; return(obj.TestProperty); }; prop.Set = (o, val) => { TestClass obj = (TestClass)o; obj.TestProperty = (int)val; }; prop.ShouldSerialize = (o, val) => (int)val != 42; ti.Properties.Add(field); ti.Properties.Add(prop); return(ti); }); JsonSerializerOptions options = new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }; options.IncludeFields = true; options.TypeInfoResolver = JsonTypeInfoResolver.Combine(resolver, options.TypeInfoResolver); TestClass originalObj = new TestClass() { TestField = "test value", TestProperty = 45, }; string json = JsonSerializer.Serialize(originalObj, options); Assert.Equal(@"{""MyTestField"":""test value"",""MyTestProperty"":45}", json); TestClass deserialized = JsonSerializer.Deserialize <TestClass>(json, options); Assert.Equal(originalObj.TestField, deserialized.TestField); Assert.Equal(originalObj.TestProperty, deserialized.TestProperty); originalObj.TestField = null; json = JsonSerializer.Serialize(originalObj, options); Assert.Equal(@"{""MyTestProperty"":45}", json); originalObj.TestField = string.Empty; json = JsonSerializer.Serialize(originalObj, options); Assert.Equal(@"{""MyTestProperty"":45}", json); deserialized = JsonSerializer.Deserialize <TestClass>(json, options); Assert.Equal(originalObj.TestField, deserialized.TestField); Assert.Equal(originalObj.TestProperty, deserialized.TestProperty); originalObj.TestField = "test value"; originalObj.TestProperty = 42; json = JsonSerializer.Serialize(originalObj, options); Assert.Equal(@"{""MyTestField"":""test value""}", json); deserialized = JsonSerializer.Deserialize <TestClass>(json, options); Assert.Equal(originalObj.TestField, deserialized.TestField); Assert.Equal(originalObj.TestProperty, deserialized.TestProperty); }
public JsonContent(TValue inputValue, JsonTypeInfo <TValue> jsonTypeInfo) { _typeInfo = jsonTypeInfo ?? throw new ArgumentNullException(nameof(jsonTypeInfo)); _typedValue = inputValue; Headers.ContentType = JsonHelpers.GetDefaultMediaType(); }
public void Initialize(Type type, JsonSerializerOptions options, bool supportContinuation) { JsonTypeInfo jsonTypeInfo = options.GetOrAddClassForRootType(type); Initialize(jsonTypeInfo, supportContinuation); }
private static TValue?ReadUsingOptions <TValue>(ReadOnlySpan <byte> utf8Json, Type returnType, JsonSerializerOptions?options) { JsonTypeInfo jsonTypeInfo = GetTypeInfo(returnType, options); return(ReadUsingMetadata <TValue>(utf8Json, jsonTypeInfo)); }
public static Task <HttpResponseMessage> PutAsJsonAsync <TValue>(this HttpClient client, Uri?requestUri, TValue value, JsonTypeInfo <TValue> jsonTypeInfo, CancellationToken cancellationToken = default) { if (client is null) { throw new ArgumentNullException(nameof(client)); } JsonContent <TValue> content = new(value, jsonTypeInfo); return(client.PutAsync(requestUri, content, cancellationToken)); }
private static TValue?ReadUsingMetadata <TValue>(JsonElement element, JsonTypeInfo jsonTypeInfo) { ReadOnlySpan <byte> utf8Json = element.GetRawValue().Span; return(ReadFromSpan <TValue>(utf8Json, jsonTypeInfo)); }
/// <summary> /// Converts the provided value into a <see cref="JsonNode"/>. /// </summary> /// <typeparam name="TValue">The type of the value to serialize.</typeparam> /// <returns>A <see cref="JsonNode"/> representation of the value.</returns> /// <param name="value">The value to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> public static JsonNode?SerializeToNode <TValue>(TValue value, JsonTypeInfo <TValue> jsonTypeInfo !!) { return(WriteNodeUsingGeneratedSerializer(value, jsonTypeInfo));
private static async Task WriteStreamAsync <TValue>( Stream utf8Json, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken) { jsonTypeInfo.EnsureConfigured(); JsonSerializerOptions options = jsonTypeInfo.Options; JsonWriterOptions writerOptions = options.GetWriterOptions(); using (var bufferWriter = new PooledByteBufferWriter(options.DefaultBufferSize)) using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) { WriteStack state = default; jsonTypeInfo = ResolvePolymorphicTypeInfo(value, jsonTypeInfo, out state.IsPolymorphicRootValue); state.Initialize(jsonTypeInfo, supportAsync: true, supportContinuation: true); state.CancellationToken = cancellationToken; bool isFinalBlock; try { do { state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold); try { isFinalBlock = WriteCore(writer, value, jsonTypeInfo, ref state); if (state.SuppressFlush) { Debug.Assert(!isFinalBlock); Debug.Assert(state.PendingTask is not null); state.SuppressFlush = false; } else { await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); bufferWriter.Clear(); } } finally { // Await any pending resumable converter tasks (currently these can only be IAsyncEnumerator.MoveNextAsync() tasks). // Note that pending tasks are always awaited, even if an exception has been thrown or the cancellation token has fired. if (state.PendingTask is not null) { try { await state.PendingTask.ConfigureAwait(false); } catch { // Exceptions should only be propagated by the resuming converter // TODO https://github.com/dotnet/runtime/issues/22144 } } // Dispose any pending async disposables (currently these can only be completed IAsyncEnumerators). if (state.CompletedAsyncDisposables?.Count > 0) { await state.DisposeCompletedAsyncDisposables().ConfigureAwait(false); } } } while (!isFinalBlock); } catch { // On exception, walk the WriteStack for any orphaned disposables and try to dispose them. await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false); throw; } } }
/// <summary> /// Convert the provided value into a <see cref="string"/>. /// </summary> /// <returns>A <see cref="string"/> representation of the value.</returns> /// <param name="value">The value to convert.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="jsonTypeInfo"/> is <see langword="null"/>. /// </exception> /// <remarks>Using a <see cref="string"/> is not as efficient as using UTF-8 /// encoding since the implementation internally uses UTF-8. See also <see cref="SerializeToUtf8Bytes{TValue}(TValue, JsonTypeInfo{TValue})"/> /// and <see cref="SerializeAsync{TValue}(IO.Stream, TValue, JsonTypeInfo{TValue}, Threading.CancellationToken)"/>. /// </remarks> public static string Serialize <TValue>(TValue value, JsonTypeInfo <TValue> jsonTypeInfo) { return(WriteUsingMetadata(value, jsonTypeInfo)); }
public override Task <T> DeserializeWrapper <T>(string json, JsonTypeInfo <T> jsonTypeInfo) { return(Task.FromResult(JsonSerializer.Deserialize(json, jsonTypeInfo))); }
internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value) { object obj; ArgumentState argumentState = state.Current.CtorArgumentState !; if (!state.SupportContinuation && !state.Current.CanContainMetadata) { // Fast path that avoids maintaining state variables. if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } ReadOnlySpan <byte> originalSpan = reader.OriginalSpan; ReadConstructorArguments(ref state, ref reader, options); obj = (T)CreateObject(ref state.Current); if (obj is IJsonOnDeserializing onDeserializing) { onDeserializing.OnDeserializing(); } if (argumentState.FoundPropertyCount > 0) { Utf8JsonReader tempReader; FoundProperty[]? properties = argumentState.FoundProperties; Debug.Assert(properties != null); for (int i = 0; i < argumentState.FoundPropertyCount; i++) { JsonPropertyInfo jsonPropertyInfo = properties[i].Item1; long resumptionByteIndex = properties[i].Item3; byte[]? propertyNameArray = properties[i].Item4; string?dataExtKey = properties[i].Item5; tempReader = new Utf8JsonReader( originalSpan.Slice(checked ((int)resumptionByteIndex)), isFinalBlock: true, state: properties[i].Item2); Debug.Assert(tempReader.TokenType == JsonTokenType.PropertyName); state.Current.JsonPropertyName = propertyNameArray; state.Current.JsonPropertyInfo = jsonPropertyInfo; state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling; bool useExtensionProperty = dataExtKey != null; if (useExtensionProperty) { Debug.Assert(jsonPropertyInfo == state.Current.JsonTypeInfo.DataExtensionProperty); state.Current.JsonPropertyNameAsString = dataExtKey; JsonSerializer.CreateDataExtensionProperty(obj, jsonPropertyInfo, options); } ReadPropertyValue(obj, ref state, ref tempReader, jsonPropertyInfo, useExtensionProperty); } FoundProperty[] toReturn = argumentState.FoundProperties !; argumentState.FoundProperties = null; ArrayPool <FoundProperty> .Shared.Return(toReturn, clearArray: true); } } else { // Slower path that supports continuation and metadata reads. JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; if (state.Current.ObjectState == StackFrameObjectState.None) { if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.StartToken; } // Read any metadata properties. if (state.Current.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata) { if (!JsonSerializer.TryReadMetadata(this, jsonTypeInfo, ref reader, ref state)) { value = default; return(false); } if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref) { value = JsonSerializer.ResolveReferenceId <T>(ref state); return(true); } state.Current.ObjectState = StackFrameObjectState.ReadMetadata; } // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type) && state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted && ResolvePolymorphicConverter(jsonTypeInfo, options, ref state) is JsonConverter polymorphicConverter) { Debug.Assert(!IsValueType); bool success = polymorphicConverter.OnTryReadAsObject(ref reader, options, ref state, out object?objectResult); value = (T)objectResult !; state.ExitPolymorphicConverter(success); return(success); } // Handle metadata post polymorphic dispatch if (state.Current.ObjectState < StackFrameObjectState.ConstructorArguments) { if (state.Current.CanContainMetadata) { JsonSerializer.ValidateMetadataForObjectConverter(this, ref reader, ref state); } if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref) { value = JsonSerializer.ResolveReferenceId <T>(ref state); return(true); } BeginRead(ref state, ref reader, options); state.Current.ObjectState = StackFrameObjectState.ConstructorArguments; } if (!ReadConstructorArgumentsWithContinuation(ref state, ref reader, options)) { value = default; return(false); } obj = (T)CreateObject(ref state.Current); if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) { Debug.Assert(state.ReferenceId != null); Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve); state.ReferenceResolver.AddReference(state.ReferenceId, obj); state.ReferenceId = null; } if (obj is IJsonOnDeserializing onDeserializing) { onDeserializing.OnDeserializing(); } if (argumentState.FoundPropertyCount > 0) { for (int i = 0; i < argumentState.FoundPropertyCount; i++) { JsonPropertyInfo jsonPropertyInfo = argumentState.FoundPropertiesAsync ![i].Item1;
public override async Task <T> DeserializeWrapper <T>(Stream utf8Json, JsonTypeInfo <T> jsonTypeInfo) { return(await JsonSerializer.DeserializeAsync <T>(utf8Json, jsonTypeInfo)); }
/// <summary> /// Used for hooking custom configuration to a newly created associated JsonTypeInfo instance. /// </summary> internal virtual void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { }
public override Task <T> DeserializeWrapper <T>(Stream utf8Json, JsonTypeInfo <T> jsonTypeInfo) { T result = JsonSerializer.Deserialize <T>(utf8Json, jsonTypeInfo); return(Task.FromResult(result)); }
internal virtual void ConfigureJsonTypeInfoUsingReflection(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { }
public override Task <string> SerializeWrapper <T>(T value, JsonTypeInfo <T> jsonTypeInfo) { JsonDocument document = JsonSerializer.SerializeToDocument(value, jsonTypeInfo); return(Task.FromResult(GetStringFromDocument(document))); }
public static void CreateObjectForListWithDefaults(bool useTypedCreateObject) { DefaultJsonTypeInfoResolver resolver = new(); resolver.Modifiers.Add((ti) => { if (ti.Type == typeof(List <int>)) { Func <List <int> > createObj = () => new List <int> { 99 }; if (useTypedCreateObject) { JsonTypeInfo <List <int> > typedTi = ti as JsonTypeInfo <List <int> >; Assert.NotNull(typedTi); typedTi.CreateObject = createObj; } else { // we want to make sure Func is not a cast to the untyped one ti.CreateObject = () => createObj(); } } }); JsonSerializerOptions options = new JsonSerializerOptions(); options.IncludeFields = true; options.TypeInfoResolver = resolver; TestClassWithLists originalObj = new TestClassWithLists() { ListProperty1 = new List <int> { 2, 3 }, ListProperty2 = new List <int> { }, }; string json = JsonSerializer.Serialize(originalObj, options); Assert.Equal("" "{" ListProperty1 ":[2,3]," ListProperty2 ":[]}" "", json); TestClassWithLists deserialized = JsonSerializer.Deserialize <TestClassWithLists>(json, options); Assert.Equal(new List <int> { 99, 2, 3 }, deserialized.ListProperty1); Assert.Equal(new List <int> { 99 }, deserialized.ListProperty2); json = @"{}"; deserialized = JsonSerializer.Deserialize <TestClassWithLists>(json, options); Assert.Null(deserialized.ListProperty1); Assert.Null(deserialized.ListProperty2); json = "" "{" ListProperty2 ":[ 123 ]}" ""; deserialized = JsonSerializer.Deserialize <TestClassWithLists>(json, options); Assert.Null(deserialized.ListProperty1); Assert.Equal(new List <int> { 99, 123 }, deserialized.ListProperty2); }