public static void RunSample() { Console.WriteLine("**Utf8JsonReader Sample***"); var jsonBytes = File.ReadAllBytes("sample.json"); var jsonSpan = jsonBytes.AsSpan(); var json = new System.Text.Json.Utf8JsonReader(jsonSpan); while (json.Read()) { Console.WriteLine(GetTokenDesc(json)); } }
public override int Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.String) { ReadOnlySpan <byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed) { return(number); } if (int.TryParse(reader.GetString(), out number)) { return(number); } } return(reader.GetInt32()); }
public override ElmTestRsReportJsonEntryFailureReasonData Read( ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { try { var equalityReader = reader; var asEquality = new ElmTestRsReportJsonEntryFailureReasonData( Equality: System.Text.Json.JsonSerializer.Deserialize <ElmTestRsReportJsonEntryFailureReasonDataEquality>(ref equalityReader)); reader = equalityReader; return(asEquality); } catch { } return(new ElmTestRsReportJsonEntryFailureReasonData( String: System.Text.Json.JsonSerializer.Deserialize <string>(ref reader))); }
private static string GetResourceString(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte, string characters) { string character = IsPrintable(nextByte) ? ((char)nextByte).ToString() : $"0x{nextByte:X2}"; string message = ""; switch (resource) { case ExceptionResource.ArrayDepthTooLarge: message = SR.Format(SR.ArrayDepthTooLarge, json.CurrentState.Options.MaxDepth); break; case ExceptionResource.MismatchedObjectArray: message = SR.Format(SR.MismatchedObjectArray, character); break; case ExceptionResource.EndOfStringNotFound: message = SR.EndOfStringNotFound; break; case ExceptionResource.RequiredDigitNotFoundAfterSign: message = SR.Format(SR.RequiredDigitNotFoundAfterSign, character); break; case ExceptionResource.RequiredDigitNotFoundAfterDecimal: message = SR.Format(SR.RequiredDigitNotFoundAfterDecimal, character); break; case ExceptionResource.RequiredDigitNotFoundEndOfData: message = SR.RequiredDigitNotFoundEndOfData; break; case ExceptionResource.ExpectedEndAfterSingleJson: message = SR.Format(SR.ExpectedEndAfterSingleJson, character); break; case ExceptionResource.ExpectedEndOfDigitNotFound: message = SR.Format(SR.ExpectedEndOfDigitNotFound, character); break; case ExceptionResource.ExpectedNextDigitEValueNotFound: message = SR.Format(SR.ExpectedNextDigitEValueNotFound, character); break; case ExceptionResource.ExpectedSeparatorAfterPropertyNameNotFound: message = SR.Format(SR.ExpectedSeparatorAfterPropertyNameNotFound, character); break; case ExceptionResource.ExpectedStartOfPropertyNotFound: message = SR.Format(SR.ExpectedStartOfPropertyNotFound, character); break; case ExceptionResource.ExpectedStartOfPropertyOrValueNotFound: message = SR.ExpectedStartOfPropertyOrValueNotFound; break; case ExceptionResource.ExpectedStartOfValueNotFound: message = SR.Format(SR.ExpectedStartOfValueNotFound, character); break; case ExceptionResource.ExpectedValueAfterPropertyNameNotFound: message = SR.ExpectedValueAfterPropertyNameNotFound; break; case ExceptionResource.FoundInvalidCharacter: message = SR.Format(SR.FoundInvalidCharacter, character); break; case ExceptionResource.InvalidEndOfJsonNonPrimitive: message = SR.Format(SR.InvalidEndOfJsonNonPrimitive, json.TokenType); break; case ExceptionResource.ObjectDepthTooLarge: message = SR.Format(SR.ObjectDepthTooLarge, json.CurrentState.Options.MaxDepth); break; case ExceptionResource.ExpectedFalse: message = SR.Format(SR.ExpectedFalse, characters); break; case ExceptionResource.ExpectedNull: message = SR.Format(SR.ExpectedNull, characters); break; case ExceptionResource.ExpectedTrue: message = SR.Format(SR.ExpectedTrue, characters); break; case ExceptionResource.InvalidCharacterWithinString: message = SR.Format(SR.InvalidCharacterWithinString, character); break; case ExceptionResource.InvalidCharacterAfterEscapeWithinString: message = SR.Format(SR.InvalidCharacterAfterEscapeWithinString, character); break; case ExceptionResource.InvalidHexCharacterWithinString: message = SR.Format(SR.InvalidHexCharacterWithinString, character); break; case ExceptionResource.EndOfCommentNotFound: message = SR.EndOfCommentNotFound; break; case ExceptionResource.ZeroDepthAtEnd: message = SR.Format(SR.ZeroDepthAtEnd); break; case ExceptionResource.ExpectedJsonTokens: message = SR.ExpectedJsonTokens; break; default: Debug.Fail($"The ExceptionResource enum value: {resource} is not part of the switch. Add the appropriate case and exception message."); break; } return(message); }
public static void ThrowJsonReaderException(ref Utf8JsonReader json, ExceptionResource resource, byte nextByte = default, ReadOnlySpan <byte> bytes = default) { throw GetJsonReaderException(ref json, resource, nextByte, bytes); }
private void VerifyRead(JsonTokenType tokenType, int depth, long bytesConsumed, ref ReadStack state, ref Utf8JsonReader reader) { switch (tokenType) { case JsonTokenType.StartArray: if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString()); } else if (depth != reader.CurrentDepth) { ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString()); } // Should not be possible to have not read anything. Debug.Assert(bytesConsumed < reader.BytesConsumed); break; case JsonTokenType.StartObject: if (reader.TokenType != JsonTokenType.EndObject) { ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString()); } else if (depth != reader.CurrentDepth) { ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString()); } // Should not be possible to have not read anything. Debug.Assert(bytesConsumed < reader.BytesConsumed); break; default: // Reading a single property value. if (reader.BytesConsumed != bytesConsumed) { ThrowHelper.ThrowJsonException_SerializationConverterRead(reader, state.JsonPath, ConverterBase.ToString()); } // Should not be possible to change token type. Debug.Assert(reader.TokenType == tokenType); break; } }
protected abstract void OnReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader);
// If this method is changed, also change JsonPropertyInfoNullable.ReadEnumerable and JsonSerializer.ApplyObjectToEnumerable public override void ReadEnumerable(JsonTokenType tokenType, ref ReadStack state, ref Utf8JsonReader reader) { Debug.Assert(ShouldDeserialize); if (state.Current.KeyName == null && (state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructibleOrKeyValuePair)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath); return; } // We need an initialized array in order to store the values. if (state.Current.IsProcessingEnumerable && state.Current.TempEnumerableValues == null && state.Current.ReturnValue == null) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath); return; } if (ValueConverter == null || !ValueConverter.TryRead(RuntimePropertyType, ref reader, out TRuntimeProperty value)) { if (state.Current.IsProcessingKeyValuePair && state.Current.KeyName == "Key") { // Handle the special case where the input KeyValuePair is of form {"Key": "MyKey", "Value": 1} // (as opposed to form {"MyKey": 1}) and the value type is not string. // If we have one, the ValueConverter failed to read the current token because it should be of type string // (we only support string keys) but we initially tried to read it as type TRuntimeProperty. // We have TRuntimeProperty not string because for deserialization, we parse the KeyValuePair as a // dictionary before creating a KeyValuePair instance in a converter-like manner with the parsed values. // Because it's dictionary-like parsing, we set the element type of the dictionary earlier on to the KeyValuePair's value // type, which led us here. // If there's no ValueConverter, the runtime type of the KeyValuePair's value is probably an object, dictionary or enumerable. JsonValueConverter <string> stringConverter = DefaultConverters <string> .s_converter; if (!stringConverter.TryRead(typeof(string), ref reader, out string strValue)) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(typeof(string), reader, state.JsonPath); } object objValue = strValue; JsonSerializer.ApplyValueToEnumerable(ref objValue, ref state, ref reader); return; } ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(RuntimePropertyType, reader, state.JsonPath); return; } if (state.Current.IsProcessingKeyValuePair) { // The value is being applied to a Dictionary<string, object>, so we need to cast to object here. object objValue = value; JsonSerializer.ApplyValueToEnumerable(ref objValue, ref state, ref reader); return; } JsonSerializer.ApplyValueToEnumerable(ref value, ref state, ref reader); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>. /// </summary> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="reader">The reader to read.</param> /// <param name="options">Options to control the serializer behavior during reading.</param> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <typeparamref name="TValue"/> is not compatible with the JSON, /// or a value could not be read from the reader. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="reader"/> is using unsupported options. /// </exception> /// <remarks> /// <para> /// If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/> /// is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the /// reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine /// the start of the value. /// </para> /// /// <para> /// Upon completion of this method <paramref name="reader"/> will be positioned at the /// final token in the JSON value. If an exception is thrown the reader is reset to /// the state it was in when the method was called. /// </para> /// /// <para> /// This method makes a copy of the data the reader acted on, so there is no caller /// requirement to maintain data integrity beyond the return of this method. /// </para> /// /// <para> /// The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict. /// Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static TValue Deserialize <TValue>(ref Utf8JsonReader reader, JsonSerializerOptions options = null) { return((TValue)ReadValueCore(ref reader, typeof(TValue), options)); }
private static void ReadValueCore(JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack readStack) { JsonReaderState state = reader.CurrentState; CheckSupportedOptions(state.Options, nameof(reader)); // Value copy to overwrite the ref on an exception and undo the destructive reads. Utf8JsonReader restore = reader; ReadOnlySpan <byte> valueSpan = default; ReadOnlySequence <byte> valueSequence = default; try { switch (reader.TokenType) { // A new reader was created and has never been read, // so we need to move to the first token. // (or a reader has terminated and we're about to throw) case JsonTokenType.None: // Using a reader loop the caller has identified a property they wish to // hydrate into a JsonDocument. Move to the value first. case JsonTokenType.PropertyName: { if (!reader.Read()) { ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.ExpectedOneCompleteToken); } break; } } switch (reader.TokenType) { // Any of the "value start" states are acceptable. case JsonTokenType.StartObject: case JsonTokenType.StartArray: { long startingOffset = reader.TokenStartIndex; if (!reader.TrySkip()) { ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.NotEnoughData); } long totalLength = reader.BytesConsumed - startingOffset; ReadOnlySequence <byte> sequence = reader.OriginalSequence; if (sequence.IsEmpty) { valueSpan = reader.OriginalSpan.Slice( checked ((int)startingOffset), checked ((int)totalLength)); } else { valueSequence = sequence.Slice(startingOffset, totalLength); } Debug.Assert( reader.TokenType == JsonTokenType.EndObject || reader.TokenType == JsonTokenType.EndArray); break; } // Single-token values case JsonTokenType.Number: case JsonTokenType.True: case JsonTokenType.False: case JsonTokenType.Null: { if (reader.HasValueSequence) { valueSequence = reader.ValueSequence; } else { valueSpan = reader.ValueSpan; } break; } // String's ValueSequence/ValueSpan omits the quotes, we need them back. case JsonTokenType.String: { ReadOnlySequence <byte> sequence = reader.OriginalSequence; if (sequence.IsEmpty) { // Since the quoted string fit in a ReadOnlySpan originally // the contents length plus the two quotes can't overflow. int payloadLength = reader.ValueSpan.Length + 2; Debug.Assert(payloadLength > 1); ReadOnlySpan <byte> readerSpan = reader.OriginalSpan; Debug.Assert( readerSpan[(int)reader.TokenStartIndex] == (byte)'"', $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}"); Debug.Assert( readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"', $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}"); valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength); } else { long payloadLength = 2; if (reader.HasValueSequence) { payloadLength += reader.ValueSequence.Length; } else { payloadLength += reader.ValueSpan.Length; } valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength); Debug.Assert( valueSequence.First.Span[0] == (byte)'"', $"Calculated sequence starts with {valueSequence.First.Span[0]}"); Debug.Assert( valueSequence.ToArray()[payloadLength - 1] == (byte)'"', $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}"); } break; } default: { byte displayByte; if (reader.HasValueSequence) { displayByte = reader.ValueSequence.First.Span[0]; } else { displayByte = reader.ValueSpan[0]; } ThrowHelper.ThrowJsonReaderException( ref reader, ExceptionResource.ExpectedStartOfValueNotFound, displayByte); break; } } } catch (JsonReaderException ex) { reader = restore; // Re-throw with Path information. ThrowHelper.ReThrowWithPath(readStack, ex); } int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length; byte[] rented = ArrayPool <byte> .Shared.Rent(length); Span <byte> rentedSpan = rented.AsSpan(0, length); try { if (valueSpan.IsEmpty) { valueSequence.CopyTo(rentedSpan); } else { valueSpan.CopyTo(rentedSpan); } JsonReaderOptions originalReaderOptions = state.Options; var newReader = new Utf8JsonReader(rentedSpan, originalReaderOptions); ReadCore(options, ref newReader, ref readStack); // The reader should have thrown if we have remaining bytes. Debug.Assert(newReader.BytesConsumed == length); } catch (JsonException) { reader = restore; throw; } finally { rentedSpan.Clear(); ArrayPool <byte> .Shared.Return(rented); } }
private static void HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.SkipProperty) { return; } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (jsonPropertyInfo == null) { jsonPropertyInfo = state.Current.JsonClassInfo.CreateRootObject(options); } else if (state.Current.JsonClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, typeof(object), options); } jsonPropertyInfo.Read(tokenType, ref state, ref reader); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>. /// </summary> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="reader">The reader to read.</param> /// <param name="options">Options to control the serializer behavior during reading.</param> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <typeparamref name="TValue"/> is not compatible with the JSON, /// or a value could not be read from the reader. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="reader"/> is using unsupported options. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <remarks> /// <para> /// If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/> /// is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the /// reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine /// the start of the value. /// </para> /// /// <para> /// Upon completion of this method <paramref name="reader"/> will be positioned at the /// final token in the JSON value. If an exception is thrown the reader is reset to /// the state it was in when the method was called. /// </para> /// /// <para> /// This method makes a copy of the data the reader acted on, so there is no caller /// requirement to maintain data integrity beyond the return of this method. /// </para> /// /// <para> /// The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict. /// Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static TValue?Deserialize <[DynamicallyAccessedMembers(MembersAccessedOnRead)] TValue>(ref Utf8JsonReader reader, JsonSerializerOptions?options = null) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } ReadStack state = default; state.Initialize(typeof(TValue), options, supportContinuation: false); return(ReadValueCore <TValue>(options, ref reader, ref state)); }