protected internal override void ReadEnumerable(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader) { if (ValueConverter != null) { Type propertyType = PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) { propertyType = Nullable.GetUnderlyingType(propertyType); } object value = ValueConverter.GetRead(ref reader, propertyType); ReadObjectState.SetReturnValue(value, options, ref current); } else { if (this is IJsonSerializerInternal <TValue> converter) { TValue value = converter.Read(ref reader); ReadObjectState.SetReturnValue(value, options, ref current); } else { throw new InvalidOperationException($"todo: there is no converter for {PropertyType}"); } } }
private static void HandleStartObject(JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { Type objType; if (current.IsEnumerable() || current.IsPropertyEnumerable()) { // An array of objects either on the current property or on a list objType = current.GetElementType(); JsonPropertyInfo propInfo = current.PropertyInfo; SetPreviousState(ref previous, current, arrayIndex++); current.Reset(); current.ClassInfo = options.GetOrAddClass(objType); current.ReturnValue = current.ClassInfo.CreateObject(); } else if (current.PropertyInfo != null) { // Nested object objType = current.PropertyInfo.PropertyType; SetPreviousState(ref previous, current, arrayIndex++); current.Reset(); current.ClassInfo = options.GetOrAddClass(objType); current.ReturnValue = current.ClassInfo.CreateObject(); } else { current.ReturnValue = current.ClassInfo.CreateObject(); } }
protected internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader) { if (ValueConverter != null) { Type propertyType = PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) { propertyType = Nullable.GetUnderlyingType(propertyType); } if (!ValueConverter.TryRead(propertyType, ref reader, out TValue value)) { throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0); } ReadObjectState.SetReturnValue(value, options, ref current); } else { if (this is IJsonValueConverter <TValue> converter) { if (!converter.TryRead(PropertyType, ref reader, out TValue value)) { throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0); } ReadObjectState.SetReturnValue(value, options, ref current); } else { throw new InvalidOperationException($"todo: there is no converter for {PropertyType}"); } } }
private static bool HandleNull(ref ReadObjectState current, JsonSerializerOptions options) { Debug.Assert(current.PropertyInfo != null); JsonPropertyInfo propertyInfo = current.PropertyInfo; if (!propertyInfo.CanBeNull) { throw new InvalidOperationException($"todo: {propertyInfo.PropertyType} can't be null"); } if (current.IsEnumerable() || current.IsPropertyEnumerable()) { ReadObjectState.SetReturnValue(null, options, ref current); return(false); } if (current.ReturnValue == null) { return(true); } if (!propertyInfo.IgnoreNullPropertyValueOnRead(options)) { current.PropertyInfo.SetValueAsObject(current.ReturnValue, null, options); } return(false); }
private static object Read( Utf8JsonReader reader, Type returnType, JsonSerializerOptions options) { if (options == null) { options = s_defaultSettings; } List <ReadObjectState> previous = null; int arrayIndex = 0; ReadObjectState current = default; JsonClassInfo classInfo = options.GetOrAddClass(returnType); current.ClassInfo = classInfo; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } Read(returnType, options, ref reader, ref current, ref previous, ref arrayIndex); return(current.ReturnValue); }
private static void HandleStartArray( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { Type arrayType = current.PropertyInfo.PropertyType; if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1)) { throw new JsonReaderException($"todo: type {arrayType.ToString()} is not convertable to array.", 0, 0); } Debug.Assert(current.IsPropertyEnumerable()); if (current.IsPropertyEnumerable()) { if (current.EnumerableCreated) { // A nested json array so push a new stack frame. Type elementType = current.ClassInfo.ElementClassInfo.GetPolicyProperty().PropertyType; SetPreviousState(ref previous, current, arrayIndex++); current.Reset(); current.ClassInfo = options.GetOrAddClass(elementType); current.PropertyInfo = current.ClassInfo.GetPolicyProperty(); current.PopStackOnEndArray = true; } else { current.EnumerableCreated = true; } // If current property is already set (from a constructor, for example) leave as-is if (current.PropertyInfo.GetValueAsObject(current.ReturnValue, options) == null) { // Create the enumerable. object value = ReadObjectState.CreateEnumerableValue(ref current, options); if (value != null) { if (current.ReturnValue != null) { current.PropertyInfo.SetValueAsObject(current.ReturnValue, value, options); } else { // Primitive arrays being returned without object current.SetReturnValue(value, options); } } } } }
public override void Read(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader) { if (ElementClassInfo != null) { // Forward the setter to the value-based JsonPropertyInfo. JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty(); propertyInfo.ReadEnumerable(options, ref current, ref reader); } else { if (ValueConverter != null) { Type propertyType = PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) { propertyType = Nullable.GetUnderlyingType(propertyType); } object value = ValueConverter.GetRead(ref reader, propertyType); if (value != null || !SkipNullValuesOnRead(options)) { SetValueAsObject(current.ReturnValue, value, options); } } else { if (this is IJsonSerializerInternal <TValue> converter) { TValue value = converter.Read(ref reader); if (current.ReturnValue == null) { current.ReturnValue = value; } else { if (value != null || !SkipNullValuesOnRead(options)) { Set(current.ReturnValue, value); } } } else { throw new InvalidOperationException($"todo: there is no converter for {PropertyType}"); } } } }
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 bool HandleEndObject(JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { object value = current.ReturnValue; if (arrayIndex > 0) { ReadObjectState previousFrame = default; GetPreviousState(ref previous, ref previousFrame, --arrayIndex); current = previousFrame; } else { current.Reset(); current.ReturnValue = value; return(true); } ReadObjectState.SetReturnValue(value, options, ref current); return(false); }
private static void ReadCore( ref JsonReaderState readerState, Type returnType, bool isFinalBlock, ReadOnlySequence <byte> buffer, JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { Utf8JsonReader reader = new Utf8JsonReader(buffer, isFinalBlock, readerState); ReadCore( options, ref reader, ref current, ref previous, ref arrayIndex); readerState = reader.CurrentState; }
private static void ReadCore( ref JsonReaderState readerState, bool isFinalBlock, byte[] buffer, int bytesToRead, JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { Utf8JsonReader reader = new Utf8JsonReader(new ReadOnlySpan <byte>(buffer, 0, bytesToRead), isFinalBlock, readerState); ReadCore( options, ref reader, ref current, ref previous, ref arrayIndex); readerState = reader.CurrentState; }
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 bool HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadObjectState current) { if (current.PropertyInfo == null) { return(false); } bool lastCall = (!current.IsEnumerable() && !current.IsPropertyEnumerable() && current.ReturnValue == null); current.PropertyInfo.Read(tokenType, options, ref current, ref reader); return(lastCall); }
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 HandleValue(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadObjectState current) { Debug.Assert(current.PropertyInfo != null); bool lastCall = (!current.IsEnumerable() && !current.IsPropertyEnumerable() && current.ReturnValue == null); current.PropertyInfo.Read(options, ref current, ref reader); return(lastCall); }
protected internal abstract void ReadEnumerable(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader);
public abstract void Read(JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader);
private static bool HandleEndArray( JsonSerializerOptions options, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { IEnumerable value = ReadObjectState.GetEnumerableValue(current); if (value == null) { // We added the items to the list property already. current.ResetProperty(); return(false); } bool lastFrame = (arrayIndex == 0); bool setPropertyDirectly; if (current.TempEnumerableValues != null) { JsonEnumerableConverter converter = current.PropertyInfo.EnumerableConverter; if (converter == null) { converter = current.ClassInfo.EnumerableConverter; } Type elementType = current.GetElementType(); value = converter.CreateFromList(elementType, (IList)value); setPropertyDirectly = true; } else { setPropertyDirectly = false; } if (current.PopStackOnEndArray) { ReadObjectState previousFrame = default; GetPreviousState(ref previous, ref previousFrame, --arrayIndex); current = previousFrame; } if (lastFrame) { if (current.ReturnValue == null) { // Returning a converted list or object. current.Reset(); current.ReturnValue = value; return(true); } else if (current.IsEnumerable()) { // Returning a non-converted list. return(true); } // else there must be an outer object, so we'll return false here. } ReadObjectState.SetReturnValue(value, options, ref current, setPropertyDirectly: setPropertyDirectly); return(false); }
public override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadObjectState current, ref Utf8JsonReader reader) { if (ElementClassInfo != null) { // Forward the setter to the value-based JsonPropertyInfo. JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty(); propertyInfo.ReadEnumerable(tokenType, options, ref current, ref reader); } else if (HasSetter) { if (ValueConverter != null) { Type propertyType = PropertyType; if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) { propertyType = Nullable.GetUnderlyingType(propertyType); } if (!ValueConverter.TryRead(propertyType, ref reader, out TValue value)) { throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0); } if (value != null || !IgnoreNullPropertyValueOnRead(options)) { SetValueAsObject(current.ReturnValue, value, options); } } else { if (this is IJsonValueConverter <TValue> converter) { if (!converter.TryRead(PropertyType, ref reader, out TValue value)) { throw new JsonReaderException("todo: unable to read value (propertypath)", 0, 0); } if (current.ReturnValue == null) { current.ReturnValue = value; } else { if (value != null || !IgnoreNullPropertyValueOnRead(options)) { Set(current.ReturnValue, value); } } } else { throw new InvalidOperationException($"todo: there is no converter for {PropertyType}"); } } } }
// todo: refactor this method to split by ClassType(Enumerable, Object, or Value) like Write() private static void ReadCore( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadObjectState current, ref List <ReadObjectState> previous, ref int arrayIndex) { while (reader.Read()) { JsonTokenType tokenType = reader.TokenType; if (tokenType >= JsonTokenType.String && tokenType <= JsonTokenType.False) { Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False); if (HandleValue(tokenType, options, ref reader, ref current)) { // todo: verify bytes read == bytes processed. return; } } else if (tokenType == JsonTokenType.PropertyName) { Debug.Assert(current.ReturnValue != default); Debug.Assert(current.ClassInfo != default); ReadOnlySpan <byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; current.PropertyInfo = current.ClassInfo.GetProperty(propertyName, current.PropertyIndex); current.PropertyIndex++; } else if (tokenType == JsonTokenType.StartObject) { HandleStartObject(options, ref current, ref previous, ref arrayIndex); } else if (tokenType == JsonTokenType.EndObject) { if (HandleEndObject(options, ref current, ref previous, ref arrayIndex)) { // todo: verify bytes read == bytes processed. return; } } else if (tokenType == JsonTokenType.StartArray) { HandleStartArray(options, ref reader, ref current, ref previous, ref arrayIndex); } else if (tokenType == JsonTokenType.EndArray) { if (HandleEndArray(options, ref current, ref previous, ref arrayIndex)) { // todo: verify bytes read == bytes processed. return; } } else if (tokenType == JsonTokenType.Null) { if (HandleNull(ref current, options)) { return; } } } return; }