internal sealed override bool OnTryWrite( Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state) { JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; object obj = value; // box once if (!state.SupportContinuation) { writer.WriteStartObject(); if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve) { if (JsonSerializer.WriteReferenceForObject(this, obj, ref state, writer) == MetadataPropertyName.Ref) { return(true); } } if (obj is IJsonOnSerializing onSerializing) { onSerializing.OnSerializing(); } List <KeyValuePair <string, JsonPropertyInfo?> > properties = jsonTypeInfo.PropertyCache !.List; for (int i = 0; i < properties.Count; i++) { JsonPropertyInfo jsonPropertyInfo = properties[i].Value !; if (jsonPropertyInfo.ShouldSerialize) { // Remember the current property for JsonPath support if an exception is thrown. state.Current.DeclaredJsonPropertyInfo = jsonPropertyInfo; state.Current.NumberHandling = jsonPropertyInfo.NumberHandling; bool success = jsonPropertyInfo.GetMemberAndWriteJson(obj, ref state, writer); // Converters only return 'false' when out of data which is not possible in fast path. Debug.Assert(success); state.Current.EndProperty(); } } // Write extension data after the normal properties. JsonPropertyInfo?dataExtensionProperty = jsonTypeInfo.DataExtensionProperty; if (dataExtensionProperty?.ShouldSerialize == true) { // Remember the current property for JsonPath support if an exception is thrown. state.Current.DeclaredJsonPropertyInfo = dataExtensionProperty; state.Current.NumberHandling = dataExtensionProperty.NumberHandling; bool success = dataExtensionProperty.GetMemberAndWriteJsonExtensionData(obj, ref state, writer); Debug.Assert(success); state.Current.EndProperty(); } writer.WriteEndObject(); } else { if (!state.Current.ProcessedStartToken) { writer.WriteStartObject(); if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve) { if (JsonSerializer.WriteReferenceForObject(this, obj, ref state, writer) == MetadataPropertyName.Ref) { return(true); } } if (obj is IJsonOnSerializing onSerializing) { onSerializing.OnSerializing(); } state.Current.ProcessedStartToken = true; } List <KeyValuePair <string, JsonPropertyInfo?> >?propertyList = jsonTypeInfo.PropertyCache !.List !; while (state.Current.EnumeratorIndex < propertyList.Count) { JsonPropertyInfo?jsonPropertyInfo = propertyList ![state.Current.EnumeratorIndex].Value;
internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value) { JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; object obj; if (state.UseFastPath) { // Fast path that avoids maintaining state variables and dealing with preserved references. if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } if (jsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo.Type, ref reader, ref state); } obj = jsonTypeInfo.CreateObject !() !; if (obj is IJsonOnDeserializing onDeserializing) { onDeserializing.OnDeserializing(); } // Process all properties. while (true) { // Read the property name or EndObject. reader.ReadWithVerify(); JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.EndObject) { break; } // Read method would have thrown if otherwise. Debug.Assert(tokenType == JsonTokenType.PropertyName); ReadOnlySpan <byte> unescapedPropertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options); JsonPropertyInfo jsonPropertyInfo = JsonSerializer.LookupProperty( obj, unescapedPropertyName, ref state, options, out bool useExtensionProperty); ReadPropertyValue(obj, ref state, ref reader, jsonPropertyInfo, useExtensionProperty); } } else { // Slower path that supports continuation and preserved references. if (state.Current.ObjectState == StackFrameObjectState.None) { if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.StartToken; } // Handle the metadata properties. if (state.Current.ObjectState < StackFrameObjectState.PropertyValue) { if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve) { if (JsonSerializer.ResolveMetadataForJsonObject <T>(ref reader, ref state, options)) { if (state.Current.ObjectState == StackFrameObjectState.ReadRefEndObject) { // This will never throw since it was previously validated in ResolveMetadataForJsonObject. value = (T)state.Current.ReturnValue !; return(true); } } else { value = default; return(false); } } } if (state.Current.ObjectState < StackFrameObjectState.CreatedObject) { if (jsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo.Type, ref reader, ref state); } obj = jsonTypeInfo.CreateObject !() !; if (obj is IJsonOnDeserializing onDeserializing) { onDeserializing.OnDeserializing(); } state.Current.ReturnValue = obj; state.Current.ObjectState = StackFrameObjectState.CreatedObject; } else { obj = state.Current.ReturnValue !; Debug.Assert(obj != null); } // Process all properties. while (true) { // Determine the property. if (state.Current.PropertyState == StackFramePropertyState.None) { state.Current.PropertyState = StackFramePropertyState.ReadName; if (!reader.Read()) { // The read-ahead functionality will do the Read(). state.Current.ReturnValue = obj; value = default; return(false); } } JsonPropertyInfo jsonPropertyInfo; if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.EndObject) { break; } // Read method would have thrown if otherwise. Debug.Assert(tokenType == JsonTokenType.PropertyName); ReadOnlySpan <byte> unescapedPropertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options); jsonPropertyInfo = JsonSerializer.LookupProperty( obj, unescapedPropertyName, ref state, options, out bool useExtensionProperty); state.Current.UseExtensionProperty = useExtensionProperty; } else { Debug.Assert(state.Current.JsonPropertyInfo != null); jsonPropertyInfo = state.Current.JsonPropertyInfo !; } if (state.Current.PropertyState < StackFramePropertyState.ReadValue) { if (!jsonPropertyInfo.ShouldDeserialize) { if (!reader.TrySkip()) { state.Current.ReturnValue = obj; value = default; return(false); } state.Current.EndProperty(); continue; } if (!ReadAheadPropertyValue(ref state, ref reader, jsonPropertyInfo)) { state.Current.ReturnValue = obj; value = default; return(false); } } if (state.Current.PropertyState < StackFramePropertyState.TryRead) { // Obtain the CLR value from the JSON and set the member. if (!state.Current.UseExtensionProperty) { if (!jsonPropertyInfo.ReadJsonAndSetMember(obj, ref state, ref reader)) { state.Current.ReturnValue = obj; value = default; return(false); } } else { if (!jsonPropertyInfo.ReadJsonAndAddExtensionProperty(obj, ref state, ref reader)) { // No need to set 'value' here since JsonElement must be read in full. state.Current.ReturnValue = obj; value = default; return(false); } } state.Current.EndProperty(); } } } if (obj is IJsonOnDeserialized onDeserialized) { onDeserialized.OnDeserialized(); } // Unbox Debug.Assert(obj != null); value = (T)obj; // Check if we are trying to build the sorted cache. if (state.Current.PropertyRefCache != null) { jsonTypeInfo.UpdateSortedPropertyCache(ref state.Current); } return(true); }
public PropertyRef(ulong key, JsonPropertyInfo info) { Key = key; Info = info; }
internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value) { bool shouldReadPreservedReferences = options.ReferenceHandling.ShouldReadPreservedReferences(); object obj; if (!state.SupportContinuation && !shouldReadPreservedReferences) { // Fast path that avoids maintaining state variables and dealing with preserved references. if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } if (state.Current.JsonClassInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoParameterlessConstructor(state.Current.JsonClassInfo.Type); } obj = state.Current.JsonClassInfo.CreateObject !() !; // Process all properties. while (true) { // Read the property name or EndObject. reader.ReadWithVerify(); JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.EndObject) { break; } if (tokenType != JsonTokenType.PropertyName) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } JsonPropertyInfo jsonPropertyInfo = JsonSerializer.LookupProperty( obj, ref reader, options, ref state, out bool useExtensionProperty); // Skip the property if not found. if (!jsonPropertyInfo.ShouldDeserialize) { reader.Skip(); state.Current.EndProperty(); continue; } // Set the property value. reader.ReadWithVerify(); if (!useExtensionProperty) { jsonPropertyInfo.ReadJsonAndSetMember(obj, ref state, ref reader); } else { jsonPropertyInfo.ReadJsonAndAddExtensionProperty(obj, ref state, ref reader); } // Ensure any exception thrown in the next read does not have a property in its JsonPath. state.Current.EndProperty(); } } else { // Slower path that supports continuation and preserved references. if (state.Current.ObjectState == StackFrameObjectState.None) { if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.StartToken; } // Handle the metadata properties. if (state.Current.ObjectState < StackFrameObjectState.MetadataPropertyValue) { if (shouldReadPreservedReferences) { if (JsonSerializer.ResolveMetadata(this, ref reader, ref state)) { if (state.Current.ObjectState == StackFrameObjectState.MetadataRefPropertyEndObject) { value = (T)state.Current.ReturnValue !; return(true); } } else { value = default !;
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.UseFastPath) { // 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.NumberHandling; 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. 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.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata) { if (!JsonSerializer.TryReadMetadata(this, ref reader, ref state)) { value = default; return(false); } state.Current.ObjectState = StackFrameObjectState.ReadMetadata; } if (state.Current.ObjectState < StackFrameObjectState.ConstructorArguments) { if (state.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 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(); 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); }