private static async Task WriteAsyncCore(object value, Type type, PipeWriter utf8Json, JsonSerializerOptions options, CancellationToken cancellationToken) { if (options == null) { options = s_defaultSettings; } var writerState = new JsonWriterState(options.WriterOptions); // Allocate the initial buffer. We don't want to use the existing buffer as there may be very few bytes left // and we won't be able to calculate flushThreshold appropriately. Memory <byte> memory = utf8Json.GetMemory(options.EffectiveBufferSize); if (value == null) { WriteNull(ref writerState, utf8Json); await utf8Json.FlushAsync(cancellationToken).ConfigureAwait(false); return; } if (type == null) { type = value.GetType(); } JsonClassInfo classInfo = options.GetOrAddClass(type); WriteObjectState current = default; current.ClassInfo = classInfo; current.CurrentValue = value; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } List <WriteObjectState> previous = null; int arrayIndex = 0; bool isFinalBlock; // For Pipes there is not a way to get current buffer total size, so we just use the initial memory size. int flushThreshold = (int)(memory.Length * .9); //todo: determine best value here (extensible?) do { isFinalBlock = Write(ref writerState, utf8Json, flushThreshold, options, ref current, ref previous, ref arrayIndex); await utf8Json.FlushAsync(cancellationToken).ConfigureAwait(false); } while (!isFinalBlock); }
private static byte[] WriteInternal(object value, Type type, JsonSerializerOptions options) { if (options == null) { options = s_defaultSettings; } byte[] result; var state = new JsonWriterState(options.WriterOptions); using (var output = new ArrayBufferWriter <byte>(options.EffectiveBufferSize)) { var writer = new Utf8JsonWriter(output, state); if (value == null) { writer.WriteNullValue(); } else { if (type == null) { type = value.GetType(); } WriteObjectState current = default; JsonClassInfo classInfo = options.GetOrAddClass(type); current.ClassInfo = classInfo; current.CurrentValue = value; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } List <WriteObjectState> previous = null; int arrayIndex = 0; Write(ref writer, -1, options, ref current, ref previous, ref arrayIndex); } writer.Flush(isFinalBlock: true); result = output.WrittenMemory.ToArray(); } return(result); }
private static async Task <T> ReadAsync <T>(PipeReader utf8Reader, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) { if (options == null) { options = s_defaultSettings; } ReadObjectState current = default; JsonClassInfo classInfo = options.GetOrAddClass(returnType); current.ClassInfo = classInfo; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } var readerState = new JsonReaderState(options: options.ReaderOptions); List <ReadObjectState> previous = null; int arrayIndex = 0; ReadResult result; do { result = await utf8Reader.ReadAsync(cancellationToken).ConfigureAwait(false); ReadOnlySequence <byte> buffer = result.Buffer; Read( ref readerState, returnType, result.IsCompleted, buffer, options, ref current, ref previous, ref arrayIndex); utf8Reader.AdvanceTo(buffer.GetPosition(readerState.BytesConsumed), buffer.End); } while (!result.IsCompleted); return((T)current.ReturnValue); }
private static void ProcessMissingProperty( ReadOnlySpan <byte> unescapedPropertyName, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.DataExtensionProperty; Debug.Assert(jsonPropertyInfo != null); Debug.Assert(state.Current.ReturnValue != null); IDictionary extensionData = (IDictionary)jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue); if (extensionData == null) { Type type = jsonPropertyInfo.DeclaredPropertyType; // Create the appropriate dictionary type. We already verified the types. Debug.Assert(type.IsGenericType); Debug.Assert(type.GetGenericArguments().Length == 2); Debug.Assert(type.GetGenericArguments()[0].UnderlyingSystemType == typeof(string)); Debug.Assert( type.GetGenericArguments()[1].UnderlyingSystemType == typeof(object) || type.GetGenericArguments()[1].UnderlyingSystemType == typeof(JsonElement)); extensionData = (IDictionary)options.GetOrAddClass(type).CreateObject(); jsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, extensionData); } JsonElement jsonElement; using (JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader)) { jsonElement = jsonDocument.RootElement.Clone(); } string keyName = JsonHelpers.Utf8GetString(unescapedPropertyName); // Currently we don't apply any naming policy. If we do, we'd have to pass it onto the JsonDocument. extensionData.Add(keyName, jsonElement); }
internal JsonPropertyInfo( Type parentClassType, Type declaredPropertyType, Type runtimePropertyType, PropertyInfo propertyInfo, Type elementType, JsonSerializerOptions options) { ParentClassType = parentClassType; DeclaredPropertyType = declaredPropertyType; RuntimePropertyType = runtimePropertyType; PropertyInfo = propertyInfo; ClassType = JsonClassInfo.GetClassType(runtimePropertyType); if (elementType != null) { ElementClassInfo = options.GetOrAddClass(elementType); } IsNullableType = runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>); CanBeNull = IsNullableType || !runtimePropertyType.IsValueType; }
internal JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; ClassType = GetClassType(type); CreateObject = options.ClassMaterializerStrategy.CreateConstructor(type); // Ignore properties on enumerable. if (ClassType == ClassType.Object) { foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { // For now we only support public getters\setters if (propertyInfo.GetMethod?.IsPublic == true || propertyInfo.SetMethod?.IsPublic == true) { AddProperty(propertyInfo.PropertyType, propertyInfo, type, options); } } } else if (ClassType == ClassType.Enumerable) { // Add a single property that maps to the class type so we can have policies applied. AddProperty(type, propertyInfo: null, type, options); // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. Type elementType = GetElementType(type); ElementClassInfo = options.GetOrAddClass(elementType); } else if (ClassType == ClassType.Value) { // Add a single property that maps to the class type so we can have policies applied. AddProperty(type, propertyInfo: null, type, options); } else { Debug.Assert(ClassType == ClassType.Unknown); // Do nothing. The type is typeof(object). } }
// todo: to minimize hashtable lookups, cache JsonClassInfo: //public JsonClassInfo ClassInfo; public virtual void Initialize( Type parentClassType, Type declaredPropertyType, Type runtimePropertyType, PropertyInfo propertyInfo, Type elementType, JsonSerializerOptions options) { ParentClassType = parentClassType; DeclaredPropertyType = declaredPropertyType; RuntimePropertyType = runtimePropertyType; PropertyInfo = propertyInfo; ClassType = JsonClassInfo.GetClassType(runtimePropertyType); if (elementType != null) { Debug.Assert(ClassType == ClassType.Enumerable || ClassType == ClassType.Dictionary); ElementClassInfo = options.GetOrAddClass(elementType); } IsNullableType = runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>); CanBeNull = IsNullableType || !runtimePropertyType.IsValueType; }
private static object ReadCore( Type returnType, JsonSerializerOptions options, ref Utf8JsonReader reader) { if (options == null) { options = s_defaultSettings; } ReadStack state = default; JsonClassInfo classInfo = options.GetOrAddClass(returnType); state.Current.JsonClassInfo = classInfo; if (classInfo.ClassType != ClassType.Object) { state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty(); } ReadCore(options, ref reader, ref state); return(state.Current.ReturnValue); }
public static object CreateEnumerableValue(ref ReadObjectState current, JsonSerializerOptions options) { // If the property has an EnumerableConverter, then we use tempEnumerableValues. if (current.PropertyInfo.EnumerableConverter != null) { current.TempEnumerableValues = new List <object>(); return(null); } Type propType = current.PropertyInfo.PropertyType; if (typeof(IList).IsAssignableFrom(propType)) { // If IList, add the members as we create them. JsonClassInfo collectionClassInfo = options.GetOrAddClass(propType); IList collection = (IList)collectionClassInfo.CreateObject(); return(collection); } else { throw new InvalidOperationException($"todo: IEnumerable type {propType.ToString()} is not convertable."); } }
public static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; // If the property has an EnumerableConverter, then we use tempEnumerableValues. if (jsonPropertyInfo.EnumerableConverter != null) { IList converterList; if (jsonPropertyInfo.ElementClassInfo.ClassType == ClassType.Value) { converterList = jsonPropertyInfo.ElementClassInfo.GetPolicyProperty().CreateConverterList(); } else { converterList = new List <object>(); } state.Current.TempEnumerableValues = converterList; return(null); } Type propType = state.Current.JsonPropertyInfo.RuntimePropertyType; if (typeof(IList).IsAssignableFrom(propType)) { // If IList, add the members as we create them. JsonClassInfo collectionClassInfo = options.GetOrAddClass(propType); IList collection = (IList)collectionClassInfo.CreateObject(); return(collection); } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propType, reader, state.PropertyPath); return(null); } }
internal static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { // If the property has an EnumerableConverter, then we use tempEnumerableValues. if (state.Current.JsonPropertyInfo.EnumerableConverter != null) { state.Current.TempEnumerableValues = new List <object>(); return(null); } Type propType = state.Current.JsonPropertyInfo.PropertyType; if (typeof(IList).IsAssignableFrom(propType)) { // If IList, add the members as we create them. JsonClassInfo collectionClassInfo = options.GetOrAddClass(propType); IList collection = (IList)collectionClassInfo.CreateObject(); return(collection); } else { ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(propType, reader, state); return(null); } }
private static void HandleStartDictionary(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingEnumerable); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } Debug.Assert(jsonPropertyInfo != null); // A nested object or dictionary so push new frame. if (state.Current.PropertyInitialized) { state.Push(); state.Current.JsonClassInfo = jsonPropertyInfo.ElementClassInfo; state.Current.InitializeJsonPropertyInfo(); state.Current.PropertyInitialized = true; ClassType classType = state.Current.JsonClassInfo.ClassType; if (classType == ClassType.Value && jsonPropertyInfo.ElementClassInfo.Type != typeof(object) && jsonPropertyInfo.ElementClassInfo.Type != typeof(JsonElement)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.JsonPath); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (state.Current.IsProcessingImmutableDictionary) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject(); } else { state.Current.ReturnValue = classInfo.CreateObject(); } return; } state.Current.PropertyInitialized = true; if (state.Current.IsProcessingImmutableDictionary) { JsonClassInfo dictionaryClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); state.Current.TempDictionaryValues = (IDictionary)dictionaryClassInfo.CreateObject(); } // Else if current property is already set (from a constructor, for example), leave as-is. else if (jsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue) == null) { // Create the dictionary. JsonClassInfo dictionaryClassInfo = jsonPropertyInfo.RuntimeClassInfo; IDictionary value = (IDictionary)dictionaryClassInfo.CreateObject(); if (value != null) { if (state.Current.ReturnValue != null) { state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value); } else { // A dictionary is being returned directly, or a nested dictionary. state.Current.SetReturnValue(value); } } } }
public void Initialize(Type type, JsonSerializerOptions options) { JsonClassInfo = options.GetOrAddClass(type); InitializeJsonPropertyInfo(); }
private static bool HandleObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert( state.Current.JsonClassInfo.ClassType == ClassType.Object || state.Current.JsonClassInfo.ClassType == ClassType.Unknown); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex); bool obtainedValue = false; object currentValue = null; // Check for polymorphism. if (jsonPropertyInfo.ClassType == ClassType.Unknown) { currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); obtainedValue = true; GetRuntimePropertyInfo(currentValue, state.Current.JsonClassInfo, ref jsonPropertyInfo, options); } state.Current.JsonPropertyInfo = jsonPropertyInfo; if (jsonPropertyInfo.ClassType == ClassType.Value) { jsonPropertyInfo.Write(options, ref state.Current, writer); state.Current.NextProperty(); return(true); } // A property that returns an enumerator keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Enumerable) { bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns a dictionary keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Dictionary) { bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns an object. if (!obtainedValue) { currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); } if (currentValue != null) { // A new stack frame is required. JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo; state.Current.NextProperty(); JsonClassInfo nextClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); state.Push(nextClassInfo, currentValue); // Set the PropertyInfo so we can obtain the property name in order to write it. state.Current.JsonPropertyInfo = previousPropertyInfo; } else { if (!jsonPropertyInfo.IgnoreNullValues) { writer.WriteNull(jsonPropertyInfo._escapedName); } state.Current.NextProperty(); } return(true); }
private static async ValueTask <TValue> ReadAsync <TValue>(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) { if (options == null) { options = s_defaultSettings; } ReadObjectState current = default; JsonClassInfo classInfo = options.GetOrAddClass(returnType); current.ClassInfo = classInfo; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } var readerState = new JsonReaderState(options: options.ReaderOptions); List <ReadObjectState> previous = null; int arrayIndex = 0; int bytesRemaining = 0; int bytesRead; // todo: switch to ArrayBuffer implementation to handle the allocs? byte[] buffer = ArrayPool <byte> .Shared.Rent(options.EffectiveBufferSize); int bufferSize = buffer.Length; bool isFinalBlock; try { do { int bytesToRead = bufferSize - bytesRemaining; bytesRead = await utf8Json.ReadAsync(buffer, bytesRemaining, bytesToRead, cancellationToken).ConfigureAwait(false); int deserializeBufferSize = bytesRemaining + bytesRead; isFinalBlock = (bytesRead == 0); ReadCore( ref readerState, isFinalBlock, buffer, deserializeBufferSize, options, ref current, ref previous, ref arrayIndex); if (isFinalBlock) { return((TValue)current.ReturnValue); } // We have to shift or expand the buffer because there wasn't enough data to complete deserialization. int bytesConsumed = (int)readerState.BytesConsumed; bytesRemaining = deserializeBufferSize - bytesConsumed; if (bytesConsumed <= (bufferSize / 2)) { // We have less than half the buffer available, double the buffer size. bufferSize = (bufferSize < HalfMaxValue) ? bufferSize * 2 : int.MaxValue; byte[] dest = ArrayPool <byte> .Shared.Rent(bufferSize); bufferSize = dest.Length; if (bytesRemaining > 0) { // Copy the unprocessed data to the new buffer while shifting the processed bytes. Buffer.BlockCopy(buffer, bytesConsumed, dest, 0, bytesRemaining); } ArrayPool <byte> .Shared.Return(buffer, clearArray : true); buffer = dest; } else if (bytesRemaining > 0) { // Shift the processed bytes to the beginning of buffer to make more room. Buffer.BlockCopy(buffer, bytesConsumed, buffer, 0, bytesRemaining); } } while (!isFinalBlock); } finally { ArrayPool <byte> .Shared.Return(buffer, clearArray : true); } throw new InvalidOperationException("todo"); }
private static async ValueTask <TValue> ReadAsync <TValue>(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) { options ??= s_defaultSettings; ReadStack state = default; JsonClassInfo classInfo = options.GetOrAddClass(returnType); state.Current.JsonClassInfo = classInfo; if (classInfo.ClassType != ClassType.Object) { state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty(); } var readerState = new JsonReaderState(options.ReaderOptions); // todo: switch to ArrayBuffer implementation to handle and simplify the allocs? byte[] buffer = ArrayPool <byte> .Shared.Rent(options.EffectiveBufferSize); int bytesInBuffer = 0; long totalBytesRead = 0; int clearMax = 0; try { while (true) { // Read from the stream until either our buffer is filled or we hit EOF. // Calling ReadCore is relatively expensive, so we minimize the number of times // we need to call it. bool isFinalBlock = false; while (true) { int bytesRead = await utf8Json.ReadAsync( #if BUILDING_INBOX_LIBRARY buffer.AsMemory(bytesInBuffer), #else buffer, bytesInBuffer, buffer.Length - bytesInBuffer, #endif cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { isFinalBlock = true; break; } totalBytesRead += bytesRead; bytesInBuffer += bytesRead; if (bytesInBuffer == buffer.Length) { break; } } if (bytesInBuffer > clearMax) { clearMax = bytesInBuffer; } // Process the data available ReadCore( ref readerState, isFinalBlock, new Span <byte>(buffer, 0, bytesInBuffer), options, ref state); Debug.Assert(readerState.BytesConsumed <= bytesInBuffer); int bytesConsumed = (int)readerState.BytesConsumed; bytesInBuffer -= bytesConsumed; if (isFinalBlock) { break; } // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization. if ((uint)bytesInBuffer > ((uint)buffer.Length / 2)) { // We have less than half the buffer available, double the buffer size. byte[] dest = ArrayPool <byte> .Shared.Rent((buffer.Length < (int.MaxValue / 2))?buffer.Length * 2 : int.MaxValue); // Copy the unprocessed data to the new buffer while shifting the processed bytes. Buffer.BlockCopy(buffer, bytesConsumed, dest, 0, bytesInBuffer); new Span <byte>(buffer, 0, clearMax).Clear(); ArrayPool <byte> .Shared.Return(buffer); clearMax = bytesInBuffer; buffer = dest; } else if (bytesInBuffer != 0) { // Shift the processed bytes to the beginning of buffer to make more room. Buffer.BlockCopy(buffer, bytesConsumed, buffer, 0, bytesInBuffer); } } } finally { // Clear only what we used and return the buffer to the pool new Span <byte>(buffer, 0, clearMax).Clear(); ArrayPool <byte> .Shared.Return(buffer); } if (bytesInBuffer != 0) { throw new JsonReaderException( SR.Format(SR.DeserializeDataRemaining, totalBytesRead, bytesInBuffer), readerState); } return((TValue)state.Current.ReturnValue); }
private static bool HandleEnumerable( JsonClassInfo elementClassInfo, JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.Enumerator == null) { if (jsonPropertyInfo._name == null) { writer.WriteStartArray(); } else { writer.WriteStartArray(jsonPropertyInfo._name); } IEnumerable enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); if (enumerable != null) { state.Current.Enumerator = enumerable.GetEnumerator(); } } if (state.Current.Enumerator != null && state.Current.Enumerator.MoveNext()) { // If the enumerator contains typeof(object), get the run-time type if (elementClassInfo.ClassType == ClassType.Object && jsonPropertyInfo.ElementClassInfo.Type == typeof(object)) { object currentValue = state.Current.Enumerator.Current; if (currentValue != null) { Type runtimeType = currentValue.GetType(); // Ignore object() instances since they are handled as an empty object. if (runtimeType != typeof(object)) { elementClassInfo = options.GetOrAddClass(runtimeType); } } } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer); } else if (state.Current.Enumerator.Current == null) { // Write a null object or enumerable. writer.WriteNullValue(); } else { // An object or another enumerator requires a new stack frame. object nextValue = state.Current.Enumerator.Current; state.Push(elementClassInfo, nextValue); } return(false); } // We are done enumerating. writer.WriteEndArray(); if (state.Current.PopStackOnEndArray) { state.Pop(); } else { state.Current.EndArray(); } return(true); }
private static bool HandleObject( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex); ClassType propertyClassType = jsonPropertyInfo.ClassType; bool obtainedValue = false; object currentValue = null; // Check for polymorphism. if (jsonPropertyInfo.RuntimePropertyType == typeof(object)) { Debug.Assert(propertyClassType == ClassType.Object); currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); obtainedValue = true; if (currentValue != null) { Type runtimeType = currentValue.GetType(); // Ignore object() instances since they are handled as an empty object. if (runtimeType != typeof(object)) { propertyClassType = JsonClassInfo.GetClassType(runtimeType); jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, runtimeType, options); } } } state.Current.JsonPropertyInfo = jsonPropertyInfo; if (propertyClassType == ClassType.Value) { jsonPropertyInfo.Write(options, ref state.Current, ref writer); state.Current.NextProperty(); return(true); } // A property that returns an enumerator keeps the same stack frame. if (propertyClassType == ClassType.Enumerable) { bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, ref writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns an object. if (!obtainedValue) { currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); } if (currentValue != null) { // A new stack frame is required. JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo; state.Current.NextProperty(); JsonClassInfo nextClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); state.Push(nextClassInfo, currentValue); // Set the PropertyInfo so we can obtain the property name in order to write it. state.Current.JsonPropertyInfo = previousPropertyInfo; } else { if (!jsonPropertyInfo.IgnoreNullPropertyValueOnWrite(options)) { writer.WriteNull(jsonPropertyInfo._escapedName); } state.Current.NextProperty(); } return(true); }
internal JsonClassInfo(Type type, JsonSerializerOptions options) { Type = type; ClassType = GetClassType(type); CreateObject = options.ClassMaterializerStrategy.CreateConstructor(type); // Ignore properties on enumerable. switch (ClassType) { case ClassType.Object: var propertyNames = new HashSet <string>(StringComparer.Ordinal); foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { // Ignore indexers if (propertyInfo.GetIndexParameters().Length > 0) { continue; } // For now we only support public getters\setters if (propertyInfo.GetMethod?.IsPublic == true || propertyInfo.SetMethod?.IsPublic == true) { JsonPropertyInfo jsonPropertyInfo = AddProperty(propertyInfo.PropertyType, propertyInfo, type, options); Debug.Assert(jsonPropertyInfo.NameUsedToCompareAsString != null); // If the JsonPropertyNameAttribute or naming policy results in collisions, throw an exception. if (!propertyNames.Add(jsonPropertyInfo.NameUsedToCompareAsString)) { ThrowHelper.ThrowInvalidOperationException_SerializerPropertyNameConflict(this, jsonPropertyInfo); } jsonPropertyInfo.ClearUnusedValuesAfterAdd(); } } DetermineExtensionDataProperty(); break; case ClassType.Enumerable: case ClassType.Dictionary: { // Add a single property that maps to the class type so we can have policies applied. JsonPropertyInfo policyProperty = AddPolicyProperty(type, options); // Use the type from the property policy to get any late-bound concrete types (from an interface like IDictionary). CreateObject = options.ClassMaterializerStrategy.CreateConstructor(policyProperty.RuntimePropertyType); // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. Type elementType = GetElementType(type, parentType: null, memberInfo: null); ElementClassInfo = options.GetOrAddClass(elementType); } break; case ClassType.ImmutableDictionary: { // Add a single property that maps to the class type so we can have policies applied. AddPolicyProperty(type, options); Type elementType = GetElementType(type, parentType: null, memberInfo: null); CreateObject = options.ClassMaterializerStrategy.CreateConstructor( typeof(Dictionary <,>).MakeGenericType(typeof(string), elementType)); // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies. ElementClassInfo = options.GetOrAddClass(elementType); } break; case ClassType.Value: case ClassType.Unknown: // Add a single property that maps to the class type so we can have policies applied. AddPolicyProperty(type, options); break; default: Debug.Fail($"Unexpected class type: {ClassType}"); break; } }