diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs index 69cd87d3cf4f..59aa8e633bff 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.cs @@ -33,7 +33,7 @@ private static void ReadCore( if (HandleValue(tokenType, options, ref reader, ref state)) { - return; + continue; } } else if (tokenType == JsonTokenType.PropertyName) @@ -82,14 +82,14 @@ private static void ReadCore( } else if (HandleValue(tokenType, options, ref reader, ref state)) { - return; + continue; } } else if (tokenType == JsonTokenType.EndObject) { if (HandleEndObject(options, ref state, ref reader)) { - return; + continue; } } else if (tokenType == JsonTokenType.StartArray) @@ -100,21 +100,21 @@ private static void ReadCore( } else if (HandleValue(tokenType, options, ref reader, ref state)) { - return; + continue; } } else if (tokenType == JsonTokenType.EndArray) { if (HandleEndArray(options, ref state, ref reader)) { - return; + continue; } } else if (tokenType == JsonTokenType.Null) { if (HandleNull(ref reader, ref state, options)) { - return; + continue; } } } diff --git a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs index 1e68821b443e..650db0a74c4e 100644 --- a/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs +++ b/src/System.Text.Json/src/System/Text/Json/Serialization/ReadStack.cs @@ -53,15 +53,21 @@ public string PropertyPath { get { - StringBuilder path = new StringBuilder(); + StringBuilder path; if (_previous == null || _index == 0) { - path.Append($"[{Current.JsonClassInfo.Type.FullName}]"); + // No path if we've walked beyond the end of our JSON document + if (Current.JsonClassInfo == null) + { + return ""; + } + + path = new StringBuilder($"[{Current.JsonClassInfo.Type.FullName}]"); } else { - path.Append($"[{_previous[0].JsonClassInfo.Type.FullName}]"); + path = new StringBuilder($"[{_previous[0].JsonClassInfo.Type.FullName}]"); for (int i = 0; i < _index; i++) { diff --git a/src/System.Text.Json/tests/Serialization/Null.ReadTests.cs b/src/System.Text.Json/tests/Serialization/Null.ReadTests.cs index c61dab68fb67..ce121432ad53 100644 --- a/src/System.Text.Json/tests/Serialization/Null.ReadTests.cs +++ b/src/System.Text.Json/tests/Serialization/Null.ReadTests.cs @@ -72,5 +72,24 @@ public static void NullLiteralObjectInput() Assert.Equal("null", obj); } } + + [Fact] + public static void NullAcceptsLeadingAndTrailingTrivia() + { + { + TestClassWithNull obj = JsonSerializer.Parse(" null"); + Assert.Null(obj); + } + + { + object obj = JsonSerializer.Parse("null "); + Assert.Null(obj); + } + + { + object obj = JsonSerializer.Parse(" null\t"); + Assert.Null(obj); + } + } } } diff --git a/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs b/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs index bcc76b72396d..ecff3a232b40 100644 --- a/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs +++ b/src/System.Text.Json/tests/Serialization/Object.ReadTests.cs @@ -15,6 +15,37 @@ public static void ReadSimpleClass() obj.Verify(); } + [Theory] + [InlineData("", " ")] + [InlineData("", "\t ")] + [InlineData("", "//Single Line Comment\r\n")] + [InlineData("", "/* Multi\nLine Comment */")] + [InlineData("", "\t\t\t\n// Both\n/* Comments */")] + [InlineData(" ", "")] + [InlineData("\t ", "")] + [InlineData(" \t", " \n")] + [InlineData("// Leading Comment\n", "")] + [InlineData("/* Multi\nLine\nComment */ ", "")] + [InlineData("/* Multi\nLine\nComment */ ", "\t// trailing comment\n ")] + public static void ReadSimpleClassIgnoresLeadingOrTrailingTrivia(string leadingTrivia, string trailingTrivia) + { + { + var options = new JsonSerializerOptions(); + options.ReadCommentHandling = JsonCommentHandling.Skip; + + SimpleTestClass obj = JsonSerializer.Parse(leadingTrivia + SimpleTestClass.s_json + trailingTrivia, options); + obj.Verify(); + } + + { + var options = new JsonSerializerOptions(); + options.ReadCommentHandling = JsonCommentHandling.Allow; + + SimpleTestClass obj = JsonSerializer.Parse(leadingTrivia + SimpleTestClass.s_json + trailingTrivia, options); + obj.Verify(); + } + } + [Fact] public static void ReadSimpleClassWithObject() { @@ -31,6 +62,18 @@ public static void ReadSimpleClassWithObject() obj.Verify(); } + [Theory] + [InlineData("", "//Single Line Comment\r\n")] + [InlineData("", "/* Multi\nLine Comment */")] + [InlineData("", "\t\t\t\n// Both\n/* Comments */")] + [InlineData("// Leading Comment\n", "")] + [InlineData("/* Multi\nLine\nComment */ ", "")] + [InlineData("/* Multi\nLine\nComment */ ", "\t// trailing comment\n ")] + public static void ReadClassWithCommentsThrowsIfDisallowed(string leadingTrivia, string trailingTrivia) + { + Assert.Throws(() => JsonSerializer.Parse(leadingTrivia + ClassWithComplexObjects.s_json + trailingTrivia)); + } + [Fact] public static void ReadSimpleClassWithObjectArray() { @@ -63,6 +106,34 @@ public static void ReadClassWithComplexObjects() obj.Verify(); } + [Theory] + [InlineData("", " ")] + [InlineData("", "\t ")] + [InlineData("", "//Single Line Comment\r\n")] + [InlineData("", "/* Multi\nLine Comment */")] + [InlineData("", "\t\t\t\n// Both\n/* Comments */")] + [InlineData(" ", "")] + [InlineData("\t ", "")] + [InlineData(" \t", " \n")] + [InlineData("// Leading Comment\n", "")] + [InlineData("/* Multi\nLine\nComment */ ", "")] + [InlineData("/* Multi\nLine\nComment */ ", "\t// trailing comment\n ")] + public static void ReadComplexClassIgnoresLeadingOrTrailingTrivia(string leadingTrivia, string trailingTrivia) + { + var options = new JsonSerializerOptions(); + options.ReadCommentHandling = JsonCommentHandling.Skip; + + ClassWithComplexObjects obj = JsonSerializer.Parse(leadingTrivia + ClassWithComplexObjects.s_json + trailingTrivia, options); + obj.Verify(); + + // Throws due to JsonDocument.TryParse not supporting Allow + //var options = new JsonSerializerOptions(); + //options.ReadCommentHandling = JsonCommentHandling.Allow; + // + //obj = JsonSerializer.Parse(leadingTrivia + ClassWithComplexObjects.s_json + trailingTrivia, options); + //obj.Verify(); + } + [Fact] public static void ReadEmpty() { diff --git a/src/System.Text.Json/tests/Serialization/Stream.ReadTests.cs b/src/System.Text.Json/tests/Serialization/Stream.ReadTests.cs index 3e372b5e8c4d..4b1a3531f4b9 100644 --- a/src/System.Text.Json/tests/Serialization/Stream.ReadTests.cs +++ b/src/System.Text.Json/tests/Serialization/Stream.ReadTests.cs @@ -32,6 +32,23 @@ public static async Task ReadSimpleObjectAsync() } } + [Fact] + public static async Task ReadSimpleObjectWithTrailingTriviaAsync() + { + byte[] data = Encoding.UTF8.GetBytes(SimpleTestClass.s_json + " /* Multi\r\nLine Comment */\t"); + using (MemoryStream stream = new MemoryStream(data)) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = 1, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + + SimpleTestClass obj = await JsonSerializer.ReadAsync(stream, options); + obj.Verify(); + } + } + [Fact] public static async Task ReadPrimitivesAsync() { @@ -46,5 +63,21 @@ public static async Task ReadPrimitivesAsync() Assert.Equal(1, i); } } + + [Fact] + public static async Task ReadPrimitivesWithTrailingTriviaAsync() + { + using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(" 1\t// Comment\r\n/* Multi\r\nLine */"))) + { + JsonSerializerOptions options = new JsonSerializerOptions + { + DefaultBufferSize = 1, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + + int i = await JsonSerializer.ReadAsync(stream, options); + Assert.Equal(1, i); + } + } } } diff --git a/src/System.Text.Json/tests/Serialization/Value.ReadTests.cs b/src/System.Text.Json/tests/Serialization/Value.ReadTests.cs index 1fdd0b05977f..0a561a34f8e6 100644 --- a/src/System.Text.Json/tests/Serialization/Value.ReadTests.cs +++ b/src/System.Text.Json/tests/Serialization/Value.ReadTests.cs @@ -34,6 +34,37 @@ public static void ReadPrimitives() Assert.Equal("Hello", s2); } + [Fact] + public static void ReadPrimitivesWithWhitespace() + { + int i = JsonSerializer.Parse(Encoding.UTF8.GetBytes(@" 1 ")); + Assert.Equal(1, i); + + int i2 = JsonSerializer.Parse("2\t"); + Assert.Equal(2, i2); + + int? i3 = JsonSerializer.Parse("\r\nnull"); + Assert.Null(i3); + + long l = JsonSerializer.Parse(Encoding.UTF8.GetBytes("\t" + long.MaxValue.ToString())); + Assert.Equal(long.MaxValue, l); + + long l2 = JsonSerializer.Parse(long.MaxValue.ToString() + " \r\n"); + Assert.Equal(long.MaxValue, l2); + + string s = JsonSerializer.Parse(Encoding.UTF8.GetBytes(@"""Hello"" ")); + Assert.Equal("Hello", s); + + string s2 = JsonSerializer.Parse(@" ""Hello"" "); + Assert.Equal("Hello", s2); + + bool b = JsonSerializer.Parse(" \ttrue "); + Assert.Equal(true, b); + + bool b2 = JsonSerializer.Parse(" false\n"); + Assert.Equal(false, b2); + } + [Fact] public static void ReadPrimitivesFail() { @@ -84,6 +115,8 @@ public static void ReadPrimitiveExtraBytesFail() { Assert.Throws(() => JsonSerializer.Parse("[2] {3}")); Assert.Throws(() => JsonSerializer.Parse(Encoding.UTF8.GetBytes(@"[2] {3}"))); + Assert.Throws(() => JsonSerializer.Parse(@"""Hello"" 42")); + Assert.Throws(() => JsonSerializer.Parse(Encoding.UTF8.GetBytes(@"""Hello"" 42"))); } [Fact]