}
_tokenType = JsonTokenType.Number;
_consumed += numberOfBytes;
+ return true;
}
else if (!ConsumeValueMultiSegment(first))
{
return false;
}
- // Cannot use HasMoreData since the JSON payload contains a single, non-primitive value
- // and hence must be handled differently.
- if (_consumed >= (uint)_buffer.Length)
- {
- goto SetIsNotPrimitiveAndReturnTrue;
- }
-
- if (_buffer[_consumed] <= JsonConstants.Space)
- {
- SkipWhiteSpaceMultiSegment();
- if (_consumed >= (uint)_buffer.Length)
- {
- goto SetIsNotPrimitiveAndReturnTrue;
- }
- }
-
- if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
- {
- if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
- {
- // This is necessary to avoid throwing when the user has 1 or more comments as the first token
- // OR if there is a comment after a single, non-primitive value.
- // In this mode, ConsumeValue consumes the comment and we need to return it as a token.
- // along with future comments in subsequeunt reads.
- if (_tokenType == JsonTokenType.Comment || _buffer[_consumed] == JsonConstants.Slash)
- {
- return true;
- }
- }
- else
- {
- Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
- goto SetIsNotPrimitiveAndReturnTrue;
- }
- }
- ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, _buffer[_consumed]);
-
- SetIsNotPrimitiveAndReturnTrue:
if (_tokenType == JsonTokenType.StartObject || _tokenType == JsonTokenType.StartArray)
{
_isNotPrimitive = true;
}
}
- if (!_isNotPrimitive)
+ if (CurrentDepth == 0)
{
ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
}
first = _buffer[_consumed];
}
- if (!_isNotPrimitive && _tokenType != JsonTokenType.None)
+ if (CurrentDepth == 0 && _tokenType != JsonTokenType.None)
{
ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
}
}
goto Done;
}
- else if (!_isNotPrimitive)
+ else if (CurrentDepth == 0)
{
ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
}
_tokenType = JsonTokenType.Number;
_consumed += numberOfBytes;
_bytePositionInLine += numberOfBytes;
+ return true;
}
else if (!ConsumeValue(first))
{
return false;
}
- // Cannot use HasMoreData since the JSON payload contains a single, non-primitive value
- // and hence must be handled differently.
- if (_consumed >= (uint)localBuffer.Length)
- {
- goto SetIsNotPrimitiveAndReturnTrue;
- }
-
- if (localBuffer[_consumed] <= JsonConstants.Space)
- {
- SkipWhiteSpace();
- if (_consumed >= (uint)localBuffer.Length)
- {
- goto SetIsNotPrimitiveAndReturnTrue;
- }
- }
-
- if (_readerOptions.CommentHandling != JsonCommentHandling.Disallow)
- {
- if (_readerOptions.CommentHandling == JsonCommentHandling.Allow)
- {
- // This is necessary to avoid throwing when the user has 1 or more comments as the first token
- // OR if there is a comment after a single, non-primitive value.
- // In this mode, ConsumeValue consumes the comment and we need to return it as a token.
- // along with future comments in subsequeunt reads.
- if (_tokenType == JsonTokenType.Comment || localBuffer[_consumed] == JsonConstants.Slash)
- {
- return true;
- }
- }
- else
- {
- Debug.Assert(_readerOptions.CommentHandling == JsonCommentHandling.Skip);
- if (_tokenType == JsonTokenType.StartObject || _tokenType == JsonTokenType.StartArray)
- {
- _isNotPrimitive = true;
- }
- goto SetIsNotPrimitiveAndReturnTrue;
- }
- }
- ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, localBuffer[_consumed]);
-
- SetIsNotPrimitiveAndReturnTrue:
if (_tokenType == JsonTokenType.StartObject || _tokenType == JsonTokenType.StartArray)
{
_isNotPrimitive = true;
}
}
- if (!_isNotPrimitive)
+ if (CurrentDepth == 0)
{
ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
}
first = _buffer[_consumed];
}
- if (!_isNotPrimitive && _tokenType != JsonTokenType.None)
+ if (CurrentDepth == 0 && _tokenType != JsonTokenType.None)
{
ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
}
}
goto Done;
}
- else if (!_isNotPrimitive)
+ else if (CurrentDepth == 0)
{
ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
}
VerifyReadLoop(ref json, null);
}
}
+
+ [Theory]
+ [MemberData(nameof(JsonTokenWithExtraValue))]
+ public static void ReadJsonTokenWithExtraValueMultiSegment(string jsonString)
+ {
+ byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);
+ ReadOnlySequence<byte> sequence = JsonTestHelper.GetSequence(utf8, 1);
+
+ foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
+ {
+ TestReadTokenWithExtra(sequence, commentHandling, isFinalBlock: false);
+ TestReadTokenWithExtra(sequence, commentHandling, isFinalBlock: true);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(JsonTokenWithExtraValueAndComments))]
+ public static void ReadJsonTokenWithExtraValueAndCommentsMultiSegment(string jsonString)
+ {
+ byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);
+ ReadOnlySequence<byte> sequence = JsonTestHelper.GetSequence(utf8, 1);
+
+ foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
+ {
+ if (commentHandling == JsonCommentHandling.Disallow)
+ {
+ continue;
+ }
+
+ TestReadTokenWithExtra(sequence, commentHandling, isFinalBlock: false);
+ TestReadTokenWithExtra(sequence, commentHandling, isFinalBlock: true);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(JsonTokenWithExtraValueAndComments))]
+ public static void ReadJsonTokenWithExtraValueAndCommentsAppendedMultiSegment(string jsonString)
+ {
+ jsonString = " /* comment */ /* comment */ " + jsonString;
+ byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);
+ ReadOnlySequence<byte> sequence = JsonTestHelper.GetSequence(utf8, 1);
+
+ foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
+ {
+ if (commentHandling == JsonCommentHandling.Disallow)
+ {
+ continue;
+ }
+
+ TestReadTokenWithExtra(sequence, commentHandling, isFinalBlock: false, commentsAppended: true);
+ TestReadTokenWithExtra(sequence, commentHandling, isFinalBlock: true, commentsAppended: true);
+ }
+ }
+
+ private static void TestReadTokenWithExtra(ReadOnlySequence<byte> sequence, JsonCommentHandling commentHandling, bool isFinalBlock, bool commentsAppended = false)
+ {
+ JsonReaderState state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = commentHandling });
+ Utf8JsonReader reader = new Utf8JsonReader(sequence, isFinalBlock, state);
+
+ if (commentsAppended && commentHandling == JsonCommentHandling.Allow)
+ {
+ Assert.True(reader.Read());
+ Assert.Equal(JsonTokenType.Comment, reader.TokenType);
+ Assert.True(reader.Read());
+ Assert.Equal(JsonTokenType.Comment, reader.TokenType);
+ }
+
+ Assert.True(reader.Read());
+ if (reader.TokenType == JsonTokenType.StartArray || reader.TokenType == JsonTokenType.StartObject)
+ {
+ Assert.True(reader.Read());
+ Assert.Contains(reader.TokenType, new[] { JsonTokenType.EndArray, JsonTokenType.EndObject });
+ }
+
+ JsonTestHelper.AssertThrows<JsonReaderException>(reader, (jsonReader) =>
+ {
+ jsonReader.Read();
+ if (commentHandling == JsonCommentHandling.Allow && jsonReader.TokenType == JsonTokenType.Comment)
+ {
+ jsonReader.Read();
+ }
+ });
+ }
}
}
}
}
+ [Theory]
+ [MemberData(nameof(JsonTokenWithExtraValue))]
+ public static void ReadJsonTokenWithExtraValue(string jsonString)
+ {
+ byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);
+
+ foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
+ {
+ TestReadTokenWithExtra(utf8, commentHandling, isFinalBlock: false);
+ TestReadTokenWithExtra(utf8, commentHandling, isFinalBlock: true);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(JsonTokenWithExtraValueAndComments))]
+ public static void ReadJsonTokenWithExtraValueAndComments(string jsonString)
+ {
+ byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);
+
+ foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
+ {
+ if (commentHandling == JsonCommentHandling.Disallow)
+ {
+ continue;
+ }
+
+ TestReadTokenWithExtra(utf8, commentHandling, isFinalBlock: false);
+ TestReadTokenWithExtra(utf8, commentHandling, isFinalBlock: true);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(JsonTokenWithExtraValueAndComments))]
+ public static void ReadJsonTokenWithExtraValueAndCommentsAppended(string jsonString)
+ {
+ jsonString = " /* comment */ /* comment */ " + jsonString;
+ byte[] utf8 = Encoding.UTF8.GetBytes(jsonString);
+
+ foreach (JsonCommentHandling commentHandling in Enum.GetValues(typeof(JsonCommentHandling)))
+ {
+ if (commentHandling == JsonCommentHandling.Disallow)
+ {
+ continue;
+ }
+
+ TestReadTokenWithExtra(utf8, commentHandling, isFinalBlock: false, commentsAppended: true);
+ TestReadTokenWithExtra(utf8, commentHandling, isFinalBlock: true, commentsAppended: true);
+ }
+ }
+
+ private static void TestReadTokenWithExtra(byte[] utf8, JsonCommentHandling commentHandling, bool isFinalBlock, bool commentsAppended = false)
+ {
+ JsonReaderState state = new JsonReaderState(options: new JsonReaderOptions { CommentHandling = commentHandling });
+ Utf8JsonReader reader = new Utf8JsonReader(utf8, isFinalBlock, state);
+
+ if (commentsAppended && commentHandling == JsonCommentHandling.Allow)
+ {
+ Assert.True(reader.Read());
+ Assert.Equal(JsonTokenType.Comment, reader.TokenType);
+ Assert.True(reader.Read());
+ Assert.Equal(JsonTokenType.Comment, reader.TokenType);
+ }
+
+ Assert.True(reader.Read());
+ if (reader.TokenType == JsonTokenType.StartArray || reader.TokenType == JsonTokenType.StartObject)
+ {
+ Assert.True(reader.Read());
+ Assert.Contains(reader.TokenType, new[] { JsonTokenType.EndArray, JsonTokenType.EndObject });
+ }
+
+ JsonTestHelper.AssertThrows<JsonReaderException>(reader, (jsonReader) =>
+ {
+ jsonReader.Read();
+ if (commentHandling == JsonCommentHandling.Allow && jsonReader.TokenType == JsonTokenType.Comment)
+ {
+ jsonReader.Read();
+ }
+ });
+ }
+
public static IEnumerable<object[]> TestCases
{
get
}
}
+ public static IEnumerable<object[]> JsonTokenWithExtraValue
+ {
+ get
+ {
+ return new List<object[]>
+ {
+ new object[] {" true 5 "},
+ new object[] {" false 5 "},
+ new object[] {" null 5 "},
+ new object[] {" 5 5 "},
+ new object[] {" 5.1234e-4 5 "},
+ new object[] {" \"hello\" 5 "},
+ new object[] {" \"hello\" \"hello\" "},
+ new object[] {" [ ] 5 "},
+ new object[] {" [ ] [] "},
+ new object[] {" [ ] {} "},
+ new object[] {" { } 5 "},
+ new object[] {" { } [] "},
+ new object[] {" { } {} "},
+ new object[] {" [ ]5 "},
+ new object[] {" [ ][] "},
+ new object[] {" [ ]{} "},
+ new object[] {" { }5 "},
+ new object[] {" { }[] "},
+ new object[] {" { }{} "},
+ new object[] {" { } 5.1234e-4"},
+ new object[] {" { } null "},
+ new object[] {" { } false "},
+ new object[] {" { } true "},
+ new object[] {" { } \"hello\" " },
+ new object[] {" { }, 5 "},
+ new object[] {" { }, [] "},
+ new object[] {" { }, {} "},
+ new object[] {" { }, 5.1234e-4"},
+ new object[] {" { }, null "},
+ new object[] {" { }, false "},
+ new object[] {" { }, true "},
+ new object[] {" { }, \"hello\" " },
+ };
+ }
+ }
+
+ public static IEnumerable<object[]> JsonTokenWithExtraValueAndComments
+ {
+ get
+ {
+ return new List<object[]>
+ {
+ new object[] {" true /* comment */ 5 "},
+ new object[] {" false /* comment */ 5 "},
+ new object[] {" null /* comment */ 5 "},
+ new object[] {" 5 /* comment */ 5 "},
+ new object[] {" 5.1234e-4 /* comment */ 5 "},
+ new object[] {" \"hello\" /* comment */ 5 "},
+ new object[] {" \"hello\" /* comment */ \"hello\" "},
+ new object[] {" \"hello\" // comment \n \"hello\" "},
+ new object[] {" [ ] /* comment */ 5 "},
+ new object[] {" [ ] /* comment */ [ ]"},
+ new object[] {" [ ] /* comment */ { }"},
+ new object[] {" [ ] // comment \n 5 "},
+ new object[] {" { } /* comment */ 5 "},
+ new object[] {" { } /* comment */ [] "},
+ new object[] {" { } /* comment */ {} "},
+ new object[] {" [ ]/* comment */5 "},
+ new object[] {" [ ]/* comment */[ ]"},
+ new object[] {" [ ]/* comment */{ }"},
+ new object[] {" [ ]// comment \n5 "},
+ new object[] {" { }/* comment */5 "},
+ new object[] {" { }/* comment */[] "},
+ new object[] {" { }/* comment */{} "},
+ new object[] {" { } /* comment */ 5.1234e-4"},
+ new object[] {" { } /* comment */ null "},
+ new object[] {" { } /* comment */ false "},
+ new object[] {" { } /* comment */ true "},
+ new object[] {" { } /* comment */ \"hello\" "},
+ new object[] {" { } // comment \n \"hello\" "},
+ new object[] {" { }, /* comment */ 5 "},
+ new object[] {" { }, /* comment */ [] "},
+ new object[] {" { }, /* comment */ {} "},
+ new object[] {" { }, /* comment */ 5.1234e-4"},
+ new object[] {" { }, /* comment */ null "},
+ new object[] {" { }, /* comment */ false "},
+ new object[] {" { }, /* comment */ true "},
+ new object[] {" { }, /* comment */ \"hello\" "},
+ new object[] {" { }, // comment \n \"hello\" "},
+ };
+ }
+ }
+
public static IEnumerable<object[]> InvalidJsonStrings
{
get