internal virtual void GetPolicies(JsonSerializerOptions options) { if (RuntimePropertyType.IsArray) { EnumerableConverter = new DefaultArrayConverter(); } else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType)) { Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType); if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType))) { EnumerableConverter = new DefaultEnumerableConverter(); } } }
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); }
public void Push(JsonClassInfo nextClassInfo, object nextValue) { Push(); Current.JsonClassInfo = nextClassInfo; Current.CurrentValue = nextValue; if (nextClassInfo.ClassType == ClassType.Enumerable) { Current.PopStackOnEndArray = true; Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty(); } else { Debug.Assert(nextClassInfo.ClassType == ClassType.Object || nextClassInfo.ClassType == ClassType.Unknown); Current.PopStackOnEndObject = true; } }
internal JsonPropertyInfo GetJsonPropertyInfoFromClassInfo(JsonClassInfo classInfo, JsonSerializerOptions options) { if (classInfo.ClassType != ClassType.Object) { return(classInfo.GetPolicyProperty()); } Type objectType = classInfo.Type; if (!_objectJsonProperties.TryGetValue(objectType, out JsonPropertyInfo propertyInfo)) { propertyInfo = JsonClassInfo.CreateProperty(objectType, objectType, null, typeof(object), options); _objectJsonProperties[objectType] = propertyInfo; } return(propertyInfo); }
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); _elementType = elementType; Options = options; IsNullableType = runtimePropertyType.IsGenericType && runtimePropertyType.GetGenericTypeDefinition() == typeof(Nullable <>); CanBeNull = IsNullableType || !runtimePropertyType.IsValueType; }
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 HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary && !state.Current.IsProcessingImmutableDictionary); if (state.Current.IsProcessingEnumerable) { // A nested object within an enumerable. Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.CreateObject is null && classInfo.ClassType == ClassType.Object) { if (classInfo.Type.IsInterface) { ThrowHelper.ThrowInvalidOperationException_DeserializePolymorphicInterface(classInfo.Type); } else { ThrowHelper.ThrowInvalidOperationException_DeserializeMissingParameterlessConstructor(classInfo.Type); } } if (state.Current.IsProcessingImmutableDictionary) { state.Current.TempDictionaryValues = (IDictionary)classInfo.CreateObject(); } else { state.Current.ReturnValue = classInfo.CreateObject(); } }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { JsonClassInfo classInfo = state.Current.JsonClassInfo; // Write the start. if (!state.Current.StartObjectWritten) { if (state.Current.JsonPropertyInfo?._escapedName == null) { writer.WriteStartObject(); } else { writer.WriteStartObject(state.Current.JsonPropertyInfo._escapedName); } state.Current.StartObjectWritten = true; } // Determine if we are done enumerating properties. if (state.Current.PropertyIndex != classInfo.PropertyCount) { HandleObject(options, writer, ref state); return(false); } writer.WriteEndObject(); if (state.Current.PopStackOnEndObject) { state.Pop(); } else { state.Current.EndObject(); } return(true); }
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; }
// 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; }
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); GetPolicies(options); } else { Debug.Assert(ClassType == ClassType.Value); // Add a single property that maps to the class type so we can have policies applied. AddProperty(type, null, type, options); } }
private static void AddNewStackFrame( JsonClassInfo nextClassInfo, object nextValue, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { SetPreviousState(ref previous, current, arrayIndex++); current.Reset(); current.ClassInfo = nextClassInfo; current.CurrentValue = nextValue; if (nextClassInfo.ClassType == ClassType.Enumerable) { current.PopStackOnEndArray = true; current.PropertyInfo = current.ClassInfo.GetPolicyProperty(); } else { Debug.Assert(nextClassInfo.ClassType == ClassType.Object); current.PopStackOnEndObject = true; } }
private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { Debug.Assert(!state.Current.IsProcessingDictionary); if (state.Current.IsProcessingEnumerable) { // A nested object within an enumerable. Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } JsonClassInfo classInfo = state.Current.JsonClassInfo; state.Current.ReturnValue = classInfo.CreateObject(); }
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."); } }
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 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 = state.Current.JsonPropertyInfo.RuntimeClassInfo; IList collection = (IList)collectionClassInfo.CreateObject(); return(collection); } else { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(propType, reader, state.JsonPath); 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.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.ThrowJsonReaderException_DeserializeUnableToConvertValue(propType, reader, state); return(null); } }
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, 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 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); }
private void DetermineSerializationCapabilities(JsonSerializerOptions options) { bool hasIgnoreAttribute = (GetAttribute <JsonIgnoreAttribute>() != null); if (hasIgnoreAttribute) { // We don't serialize or deserialize. return; } if (ClassType != ClassType.Enumerable) { // We serialize if there is a getter + no [Ignore] attribute + not ignoring readonly properties. ShouldSerialize = HasGetter && (HasSetter || !options.IgnoreReadOnlyProperties); // We deserialize if there is a setter + no [Ignore] attribute. ShouldDeserialize = HasSetter; } else { if (HasGetter) { if (HasSetter) { ShouldDeserialize = true; } else if (RuntimePropertyType.IsAssignableFrom(typeof(IList))) { ShouldDeserialize = true; } //else //{ // // todo: future feature that allows non-List types (e.g. from System.Collections.Immutable) to have converters. //} } //else if (HasSetter) //{ // // todo: Special case where there is no getter but a setter (and an EnumerableConverter) //} if (ShouldDeserialize) { ShouldSerialize = HasGetter; if (RuntimePropertyType.IsArray) { EnumerableConverter = s_jsonArrayConverter; } else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType)) { Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType); // If the property type only has interface(s) exposed by JsonEnumerableT<T> then use JsonEnumerableT as the converter. if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType))) { EnumerableConverter = s_jsonEnumerableConverter; } } } else { ShouldSerialize = HasGetter && !options.IgnoreReadOnlyProperties; } } }
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 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()) { // Check for polymorphism. if (elementClassInfo.ClassType == ClassType.Unknown) { object currentValue = state.Current.Enumerator.Current; GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); } 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 HandleEnumerable( JsonClassInfo elementClassInfo, JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { Debug.Assert(current.PropertyInfo.ClassType == ClassType.Enumerable); JsonPropertyInfo propertyInfo = current.PropertyInfo; if (current.Enumerator == null) { if (propertyInfo.Name == null) { writer.WriteStartArray(); } else { writer.WriteStartArray(propertyInfo.Name); } IEnumerable enumerable = (IEnumerable)propertyInfo.GetValueAsObject(current.CurrentValue, options); if (enumerable != null) { current.Enumerator = enumerable.GetEnumerator(); } } if (current.Enumerator != null && current.Enumerator.MoveNext()) { if (elementClassInfo.ClassType == ClassType.Value) { propertyInfo.Write(options, ref current, ref writer); } else { // An object or another enumerator requires a new stack frame JsonClassInfo nextClassInfo = propertyInfo.ElementClassInfo; object nextValue = current.Enumerator.Current; AddNewStackFrame(nextClassInfo, nextValue, ref current, ref previous, ref arrayIndex); } return(false); } // We are done enumerating. writer.WriteEndArray(); if (current.PopStackOnEndArray) { WriteObjectState previousFrame = default; GetPreviousState(ref previous, ref previousFrame, --arrayIndex); current = previousFrame; } else { current.EndArray(); } return(true); }
public void Initialize(Type type, JsonSerializerOptions options) { JsonClassInfo = options.GetOrAddClass(type); InitializeJsonPropertyInfo(); }
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); } } } }
private static bool HandleDictionary( JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.Enumerator == null) { IEnumerable enumerable; enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); if (enumerable == null) { // Write a null object or enumerable. state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, writeNull: true); return(true); } state.Current.Enumerator = enumerable.GetEnumerator(); state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer); } if (state.Current.Enumerator.MoveNext()) { // Handle DataExtension. if (ReferenceEquals(jsonPropertyInfo, state.Current.JsonClassInfo.DataExtensionProperty)) { WriteExtensionData(writer, ref state.Current); } else { // Check for polymorphism. if (elementClassInfo.ClassType == ClassType.Unknown) { object currentValue = ((IDictionaryEnumerator)state.Current.Enumerator).Entry.Value; GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.GetPolicyProperty().WriteDictionary(ref state.Current, writer); } else if (state.Current.Enumerator.Current == null) { writer.WriteNull(jsonPropertyInfo.Name); } else { // An object or another enumerator requires a new stack frame. var enumerator = (IDictionaryEnumerator)state.Current.Enumerator; object value = enumerator.Value; state.Push(elementClassInfo, value); state.Current.KeyName = (string)enumerator.Key; } } return(false); } // We are done enumerating. writer.WriteEndObject(); if (state.Current.PopStackOnEnd) { state.Pop(); } else { state.Current.EndDictionary(); } return(true); }
private static void HandleStartObject(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.Skip()) { state.Push(); state.Current.Drain = true; return; } if (state.Current.IsProcessingEnumerable) { Type objType = state.Current.GetElementType(); state.Push(); state.Current.Initialize(objType, options); } else if (state.Current.JsonPropertyInfo != null) { if (state.Current.IsDictionary) { // Verify that the Dictionary can be deserialized by having <string> as first generic argument. Type[] args = state.Current.JsonClassInfo.Type.GetGenericArguments(); if (args.Length == 0 || args[0].UnderlyingSystemType != typeof(string)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.PropertyPath); } if (state.Current.ReturnValue == null) { // The Dictionary created below will be returned to corresponding Parse() etc method. // Ensure any nested array creates a new frame. state.Current.EnumerableCreated = true; } else { ClassType classType = state.Current.JsonClassInfo.ElementClassInfo.ClassType; // Verify that the second parameter is not a value. if (state.Current.JsonClassInfo.ElementClassInfo.ClassType == ClassType.Value) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(state.Current.JsonClassInfo.Type, reader, state.PropertyPath); } // A nested object, dictionary or enumerable. JsonClassInfo classInfoTemp = state.Current.JsonClassInfo; state.Push(); state.Current.JsonClassInfo = classInfoTemp.ElementClassInfo; state.Current.InitializeJsonPropertyInfo(); } } else { // Nested object. Type objType = state.Current.JsonPropertyInfo.RuntimePropertyType; state.Push(); state.Current.Initialize(objType, options); } } JsonClassInfo classInfo = state.Current.JsonClassInfo; state.Current.ReturnValue = classInfo.CreateObject(); }
private void DetermineSerializationCapabilities(JsonSerializerOptions options) { if (ClassType != ClassType.Enumerable && ClassType != ClassType.Dictionary) { // We serialize if there is a getter + not ignoring readonly properties. ShouldSerialize = HasGetter && (HasSetter || !options.IgnoreReadOnlyProperties); // We deserialize if there is a setter. ShouldDeserialize = HasSetter; } else { if (HasGetter) { if (HasSetter) { ShouldDeserialize = true; } else if (!RuntimePropertyType.IsArray && (typeof(IList).IsAssignableFrom(RuntimePropertyType) || typeof(IDictionary).IsAssignableFrom(RuntimePropertyType))) { ShouldDeserialize = true; } } //else if (HasSetter) //{ // // todo: Special case where there is no getter but a setter (and an EnumerableConverter) //} if (ShouldDeserialize) { ShouldSerialize = HasGetter; if (RuntimePropertyType.IsArray) { EnumerableConverter = s_jsonArrayConverter; } else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType)) { Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType, ParentClassType, PropertyInfo); // If the property type only has interface(s) exposed by JsonEnumerableT<T> then use JsonEnumerableT as the converter. if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType))) { EnumerableConverter = s_jsonEnumerableConverter; } // Else if IList can't be assigned from the property type (we populate and return an IList directly) // and the type can be constructed with an IEnumerable<T>, then use the // IEnumerableConstructible converter to create the instance. else if (!typeof(IList).IsAssignableFrom(RuntimePropertyType) && RuntimePropertyType.GetConstructor(new Type[] { typeof(List <>).MakeGenericType(elementType) }) != null) { EnumerableConverter = s_jsonIEnumerableConstuctibleConverter; } // Else if it's a System.Collections.Immutable type with one generic argument. else if (RuntimePropertyType.IsGenericType && RuntimePropertyType.FullName.StartsWith(DefaultImmutableConverter.ImmutableNamespace) && RuntimePropertyType.GetGenericArguments().Length == 1) { EnumerableConverter = s_jsonImmutableConverter; ((DefaultImmutableConverter)EnumerableConverter).RegisterImmutableCollectionType(RuntimePropertyType, elementType, options); } } } else { ShouldSerialize = HasGetter && !options.IgnoreReadOnlyProperties; } } }