From 45818a8f9466ed83a6ac5e170e292756f0cbac30 Mon Sep 17 00:00:00 2001 From: etemi <40637181+etemi@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:46:58 +0200 Subject: [PATCH 1/5] Make GetPropertyCount public and fix its return value --- .../System.Text.Json/ref/System.Text.Json.cs | 1 + .../Json/Document/JsonDocument.MetadataDb.cs | 2 +- .../System/Text/Json/Document/JsonDocument.cs | 10 ++++--- .../System/Text/Json/Document/JsonElement.cs | 2 +- .../JsonDocumentTests.cs | 26 +++++++++++++++++++ .../Serialization/Stream.ReadTests.cs | 3 +++ 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index d1394f939f1a1..a75a8bb9dd264 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -66,6 +66,7 @@ public readonly partial struct JsonElement public System.Text.Json.JsonElement GetProperty(System.ReadOnlySpan utf8PropertyName) { throw null; } public System.Text.Json.JsonElement GetProperty(System.ReadOnlySpan propertyName) { throw null; } public System.Text.Json.JsonElement GetProperty(string propertyName) { throw null; } + public int GetPropertyCount() { throw null; } public string GetRawText() { throw null; } [System.CLSCompliantAttribute(false)] public sbyte GetSByte() { throw null; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs index a53df982c12eb..dca61c889c89d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.cs @@ -68,7 +68,7 @@ public sealed partial class JsonDocument // * 31 bits for token offset // * Second int // * Top bit is unassigned / always clear - // * 31 bits for the token length (always 1, effectively unassigned) + // * 31 bits for the number of properties in this object // * Third int // * 4 bits JsonTokenType // * 28 bits for the number of rows until the next value (never 0) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs index ccf524fc40f3a..bc9a65fef28bd 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs @@ -950,6 +950,7 @@ private static void Parse( ref StackRowStack stack) { bool inArray = false; + int numberOfProperties = 0; int arrayItemsCount = 0; int numberOfRowsForMembers = 0; int numberOfRowsForValues = 0; @@ -977,9 +978,10 @@ private static void Parse( numberOfRowsForValues++; database.Append(tokenType, tokenStart, DbRow.UnknownSize); - var row = new StackRow(numberOfRowsForMembers + 1); + var row = new StackRow(numberOfProperties, numberOfRowsForMembers + 1); stack.Push(row); numberOfRowsForMembers = 0; + numberOfProperties = 0; } else if (tokenType == JsonTokenType.EndObject) { @@ -987,7 +989,7 @@ private static void Parse( numberOfRowsForValues++; numberOfRowsForMembers++; - database.SetLength(rowIndex, numberOfRowsForMembers); + database.SetLength(rowIndex, numberOfProperties); int newRowIndex = database.Length; database.Append(tokenType, tokenStart, reader.ValueSpan.Length); @@ -995,7 +997,8 @@ private static void Parse( database.SetNumberOfRows(newRowIndex, numberOfRowsForMembers); StackRow row = stack.Pop(); - numberOfRowsForMembers += row.SizeOrLength; + numberOfRowsForMembers += row.NumberOfRows; + numberOfProperties = row.SizeOrLength; } else if (tokenType == JsonTokenType.StartArray) { @@ -1047,6 +1050,7 @@ private static void Parse( { numberOfRowsForValues++; numberOfRowsForMembers++; + numberOfProperties++; // Adding 1 to skip the start quote will never overflow Debug.Assert(tokenStart < int.MaxValue); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index da72d147abd35..e71c9ae3aa9fe 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -96,7 +96,7 @@ public int GetArrayLength() /// /// The parent has been disposed. /// - internal int GetPropertyCount() + public int GetPropertyCount() { CheckValidInstance(); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs index 518ab0f46e3cf..62a5de7df62d3 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs @@ -681,6 +681,7 @@ public static void ParseSimpleObject() string city = parsedObject.GetProperty("city").GetString(); int zip = parsedObject.GetProperty("zip").GetInt32(); + Assert.Equal(7, parsedObject.GetPropertyCount()); Assert.True(parsedObject.TryGetProperty("age", out JsonElement age2)); Assert.Equal(30, age2.GetInt32()); @@ -704,6 +705,7 @@ public static void ParseNestedJson() Assert.Equal(1, parsedObject.GetArrayLength()); JsonElement person = parsedObject[0]; + Assert.Equal(5, person.GetPropertyCount()); double age = person.GetProperty("age").GetDouble(); string first = person.GetProperty("first").GetString(); string last = person.GetProperty("last").GetString(); @@ -902,6 +904,7 @@ public static void ReadNumber_1Byte(sbyte value) Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -999,6 +1002,7 @@ public static void ReadNumber_2Bytes(short value) Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1091,6 +1095,7 @@ public static void ReadSmallInteger(int value) Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1194,6 +1199,7 @@ public static void ReadMediumInteger(long value) Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1267,6 +1273,7 @@ public static void ReadLargeInteger(ulong value) Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1340,6 +1347,7 @@ public static void ReadTooLargeInteger() Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1379,6 +1387,7 @@ public static void ReadDateTimeAndDateTimeOffset(string jsonString, string expec Assert.Throws(() => root.GetInt64()); Assert.Throws(() => root.GetUInt64()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1462,6 +1471,7 @@ public static void ReadGuid(string jsonString, string expectedStr) Assert.Throws(() => root.GetInt64()); Assert.Throws(() => root.GetUInt64()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1564,6 +1574,7 @@ public static void ReadNonInteger(string str, double expectedDouble, float expec Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1654,6 +1665,7 @@ public static void ReadTooPreciseDouble() Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetBoolean()); @@ -1735,6 +1747,7 @@ public static void CheckUseAfterDispose() Assert.Throws(() => root.ValueKind); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetDouble()); @@ -1793,6 +1806,7 @@ public static void CheckUseDefault() Assert.Equal(JsonValueKind.Undefined, root.ValueKind); Assert.Throws(() => root.GetArrayLength()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); Assert.Throws(() => root.GetDouble()); @@ -2442,6 +2456,7 @@ public static void GetRawText() using (JsonDocument doc = JsonDocument.Parse(json)) { + Assert.Equal(6, doc.RootElement.GetPropertyCount()); JsonElement.ObjectEnumerator enumerator = doc.RootElement.EnumerateObject(); Assert.True(enumerator.MoveNext(), "Move to first property"); JsonProperty property = enumerator.Current; @@ -2449,6 +2464,7 @@ public static void GetRawText() Assert.Equal(" weird property name", property.Name); string rawText = property.ToString(); int crCount = rawText.Count(c => c == '\r'); + Assert.Equal(2, property.Value.GetPropertyCount()); Assert.Equal(128 + crCount, rawText.Length); Assert.Equal('\"', rawText[0]); Assert.Equal(' ', rawText[1]); @@ -3437,6 +3453,16 @@ public static void ParseValue_AllowMultipleValues_TrailingContent() JsonTestHelper.AssertThrows(ref reader, (ref Utf8JsonReader reader) => reader.Read()); } + [Theory] + [InlineData("""{ "foo" : [1], "test": false, "bar" : { "nested": 3 } }""", 3)] + [InlineData("""{ "foo" : [1,2,3,4] }""", 1)] + [InlineData("""{}""", 0)] + [InlineData("""{ "foo" : {"nested:" : {"nested": 1, "bla": [1, 2, {"bla": 3}] } }, "test": true, "foo2" : {"nested:" : {"nested": 1, "bla": [1, 2, {"bla": 3}] } }}""", 3)] + public static void TestGetPropertyCount(string json, int expectedCount) { + JsonElement element = JsonSerializer.Deserialize(json); + Assert.Equal(expectedCount, element.GetPropertyCount()); + } + [Fact] public static void EnsureResizeSucceeds() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs index f689cf296ead1..e296ee97c8d06 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.ReadTests.cs @@ -220,11 +220,14 @@ public async Task TestBOMWithShortAndLongBuffers(Stream stream, int count, int e void VerifyElement(int index) { Assert.Equal(JsonValueKind.Object, value[index].GetProperty("Test").ValueKind); + Assert.Equal(0, value[index].GetProperty("Test").GetPropertyCount()); Assert.False(value[index].GetProperty("Test").EnumerateObject().MoveNext()); Assert.Equal(JsonValueKind.Array, value[index].GetProperty("Test2").ValueKind); Assert.Equal(0, value[index].GetProperty("Test2").GetArrayLength()); Assert.Equal(JsonValueKind.Object, value[index].GetProperty("Test3").ValueKind); Assert.Equal(JsonValueKind.Object, value[index].GetProperty("Test3").GetProperty("Value").ValueKind); + Assert.Equal(1, value[index].GetProperty("Test3").GetPropertyCount()); + Assert.Equal(0, value[index].GetProperty("Test3").GetProperty("Value").GetPropertyCount()); Assert.False(value[index].GetProperty("Test3").GetProperty("Value").EnumerateObject().MoveNext()); Assert.Equal(0, value[index].GetProperty("PersonType").GetInt32()); Assert.Equal(2, value[index].GetProperty("Id").GetInt32()); From 0c6fc09ba6639b351f7bba793c92add711f83227 Mon Sep 17 00:00:00 2001 From: etemi <40637181+etemi@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:26:07 +0200 Subject: [PATCH 2/5] Change order of assignments to match array counterpart --- .../src/System/Text/Json/Document/JsonDocument.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs index bc9a65fef28bd..feb28b8f7aacd 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs @@ -980,8 +980,8 @@ private static void Parse( database.Append(tokenType, tokenStart, DbRow.UnknownSize); var row = new StackRow(numberOfProperties, numberOfRowsForMembers + 1); stack.Push(row); - numberOfRowsForMembers = 0; numberOfProperties = 0; + numberOfRowsForMembers = 0; } else if (tokenType == JsonTokenType.EndObject) { @@ -997,8 +997,8 @@ private static void Parse( database.SetNumberOfRows(newRowIndex, numberOfRowsForMembers); StackRow row = stack.Pop(); - numberOfRowsForMembers += row.NumberOfRows; numberOfProperties = row.SizeOrLength; + numberOfRowsForMembers += row.NumberOfRows; } else if (tokenType == JsonTokenType.StartArray) { From ea276ea6ba43a52c8380bf8d6c074d815a12d289 Mon Sep 17 00:00:00 2001 From: etemi <40637181+etemi@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:11:29 +0200 Subject: [PATCH 3/5] Reuse local variable --- .../System/Text/Json/Document/JsonDocument.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs index feb28b8f7aacd..7b3ffa246a90c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs @@ -950,8 +950,7 @@ private static void Parse( ref StackRowStack stack) { bool inArray = false; - int numberOfProperties = 0; - int arrayItemsCount = 0; + int arrayItemsOrPropertyCount = 0; int numberOfRowsForMembers = 0; int numberOfRowsForValues = 0; @@ -973,14 +972,14 @@ private static void Parse( { if (inArray) { - arrayItemsCount++; + arrayItemsOrPropertyCount++; } numberOfRowsForValues++; database.Append(tokenType, tokenStart, DbRow.UnknownSize); - var row = new StackRow(numberOfProperties, numberOfRowsForMembers + 1); + var row = new StackRow(arrayItemsOrPropertyCount, numberOfRowsForMembers + 1); stack.Push(row); - numberOfProperties = 0; + arrayItemsOrPropertyCount = 0; numberOfRowsForMembers = 0; } else if (tokenType == JsonTokenType.EndObject) @@ -989,7 +988,7 @@ private static void Parse( numberOfRowsForValues++; numberOfRowsForMembers++; - database.SetLength(rowIndex, numberOfProperties); + database.SetLength(rowIndex, arrayItemsOrPropertyCount); int newRowIndex = database.Length; database.Append(tokenType, tokenStart, reader.ValueSpan.Length); @@ -997,21 +996,21 @@ private static void Parse( database.SetNumberOfRows(newRowIndex, numberOfRowsForMembers); StackRow row = stack.Pop(); - numberOfProperties = row.SizeOrLength; + arrayItemsOrPropertyCount = row.SizeOrLength; numberOfRowsForMembers += row.NumberOfRows; } else if (tokenType == JsonTokenType.StartArray) { if (inArray) { - arrayItemsCount++; + arrayItemsOrPropertyCount++; } numberOfRowsForMembers++; database.Append(tokenType, tokenStart, DbRow.UnknownSize); - var row = new StackRow(arrayItemsCount, numberOfRowsForValues + 1); + var row = new StackRow(arrayItemsOrPropertyCount, numberOfRowsForValues + 1); stack.Push(row); - arrayItemsCount = 0; + arrayItemsOrPropertyCount = 0; numberOfRowsForValues = 0; } else if (tokenType == JsonTokenType.EndArray) @@ -1020,7 +1019,7 @@ private static void Parse( numberOfRowsForValues++; numberOfRowsForMembers++; - database.SetLength(rowIndex, arrayItemsCount); + database.SetLength(rowIndex, arrayItemsOrPropertyCount); database.SetNumberOfRows(rowIndex, numberOfRowsForValues); // If the array item count is (e.g.) 12 and the number of rows is (e.g.) 13 @@ -1033,7 +1032,7 @@ private static void Parse( // This check is similar to tracking the start array and painting it when // StartObject or StartArray is encountered, but avoids the mixed state // where "UnknownSize" implies "has complex children". - if (arrayItemsCount + 1 != numberOfRowsForValues) + if (arrayItemsOrPropertyCount + 1 != numberOfRowsForValues) { database.SetHasComplexChildren(rowIndex); } @@ -1043,14 +1042,14 @@ private static void Parse( database.SetNumberOfRows(newRowIndex, numberOfRowsForValues); StackRow row = stack.Pop(); - arrayItemsCount = row.SizeOrLength; + arrayItemsOrPropertyCount = row.SizeOrLength; numberOfRowsForValues += row.NumberOfRows; } else if (tokenType == JsonTokenType.PropertyName) { numberOfRowsForValues++; numberOfRowsForMembers++; - numberOfProperties++; + arrayItemsOrPropertyCount++; // Adding 1 to skip the start quote will never overflow Debug.Assert(tokenStart < int.MaxValue); @@ -1072,7 +1071,7 @@ private static void Parse( if (inArray) { - arrayItemsCount++; + arrayItemsOrPropertyCount++; } if (tokenType == JsonTokenType.String) From 77a6f19e18b58b65a2ca017e933e83f3b3d4f677 Mon Sep 17 00:00:00 2001 From: etemi <40637181+etemi@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:43:27 +0200 Subject: [PATCH 4/5] Show that GetPropertyCount() and EnumerateObject().Count() are the same at every object node --- .../JsonDocumentTests.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs index 62a5de7df62d3..412d859aca5f1 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs @@ -3458,11 +3458,34 @@ public static void ParseValue_AllowMultipleValues_TrailingContent() [InlineData("""{ "foo" : [1,2,3,4] }""", 1)] [InlineData("""{}""", 0)] [InlineData("""{ "foo" : {"nested:" : {"nested": 1, "bla": [1, 2, {"bla": 3}] } }, "test": true, "foo2" : {"nested:" : {"nested": 1, "bla": [1, 2, {"bla": 3}] } }}""", 3)] - public static void TestGetPropertyCount(string json, int expectedCount) { + public static void TestGetPropertyCount(string json, int expectedCount) + { JsonElement element = JsonSerializer.Deserialize(json); Assert.Equal(expectedCount, element.GetPropertyCount()); } + [Fact] + public static void VerifyGetPropertyCountUsingEnumerateObject() + { + using (JsonDocument doc = JsonDocument.Parse(SR.ProjectLockJson)) + { + CheckPropertyCountAgainstEnumerateObject(doc.RootElement); + } + + void CheckPropertyCountAgainstEnumerateObject(JsonElement obj) + { + Assert.Equal(obj.EnumerateObject().Count(), obj.GetPropertyCount()); + + foreach (JsonProperty prop in obj.EnumerateObject()) + { + if (prop.Value.ValueKind == JsonValueKind.Object) + { + CheckPropertyCountAgainstEnumerateObject(prop.Value); + } + } + } + } + [Fact] public static void EnsureResizeSucceeds() { From 2b0d0fb26e020eff5589c09d12a251cf62bcd087 Mon Sep 17 00:00:00 2001 From: etemi <40637181+etemi@users.noreply.github.com> Date: Fri, 16 Aug 2024 22:42:29 +0200 Subject: [PATCH 5/5] Fix test to also check objects inside arrays Also added checks for `GetArrayLength` --- .../JsonDocumentTests.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs index 412d859aca5f1..8be93677470ec 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonDocumentTests.cs @@ -3465,22 +3465,29 @@ public static void TestGetPropertyCount(string json, int expectedCount) } [Fact] - public static void VerifyGetPropertyCountUsingEnumerateObject() + public static void VerifyGetPropertyCountAndArrayLengthUsingEnumerateMethods() { using (JsonDocument doc = JsonDocument.Parse(SR.ProjectLockJson)) { - CheckPropertyCountAgainstEnumerateObject(doc.RootElement); + CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(doc.RootElement); } - void CheckPropertyCountAgainstEnumerateObject(JsonElement obj) + void CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(JsonElement elem) { - Assert.Equal(obj.EnumerateObject().Count(), obj.GetPropertyCount()); - - foreach (JsonProperty prop in obj.EnumerateObject()) + if (elem.ValueKind == JsonValueKind.Object) + { + Assert.Equal(elem.EnumerateObject().Count(), elem.GetPropertyCount()); + foreach (JsonProperty prop in elem.EnumerateObject()) + { + CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(prop.Value); + } + } + else if (elem.ValueKind == JsonValueKind.Array) { - if (prop.Value.ValueKind == JsonValueKind.Object) + Assert.Equal(elem.EnumerateArray().Count(), elem.GetArrayLength()); + foreach (JsonElement item in elem.EnumerateArray()) { - CheckPropertyCountAgainstEnumerateObject(prop.Value); + CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(item); } } }