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 d1394f939f1a1d..a75a8bb9dd264d 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.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs index ccf524fc40f3a4..0ba106ce81c289 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 @@ -148,7 +148,29 @@ internal int GetPropertyCount(int index) CheckExpectedType(JsonTokenType.StartObject, row.TokenType); - return row.SizeOrLength; + int propertyCount = 0; + int objectOffset = index + DbRow.Size; + int innerDepth = 0; + for (; objectOffset < _parsedData.Length; objectOffset += DbRow.Size) + { + row = _parsedData.Get(objectOffset); + if (row.TokenType is JsonTokenType.StartObject or JsonTokenType.StartArray) + { + innerDepth++; + continue; + } + + if (row.TokenType is JsonTokenType.EndObject or JsonTokenType.EndArray) + { + innerDepth--; + continue; + } + + if (innerDepth is 0 && row.TokenType is JsonTokenType.PropertyName) + propertyCount++; + } + + return propertyCount; } internal JsonElement GetArrayIndexElement(int currentIndex, int arrayIndex) 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 da72d147abd350..e71c9ae3aa9fe9 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 518ab0f46e3cf5..d74652cc838696 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 @@ -2,19 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; +using System.Buffers.Text; using System.Collections; -using System.Globalization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; +using System.Globalization; using System.IO; -using Xunit; -using System.Buffers.Text; using System.IO.Tests; using System.Linq; -using System.Runtime.InteropServices; -using System.Threading.Tasks; using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; namespace System.Text.Json.Tests { @@ -665,13 +664,24 @@ public static void ParseArray() } } + [Theory] + [InlineData("""{"foo":1,"bar":2}""")] + [InlineData("""{"foo":1,"bar":{"a":2}}""")] + [InlineData("""{"foo":1,"bar":{"a":{"b":2}}}""")] + [InlineData("""{"foo":1,"bar":[{"a":2}]}""")] + [InlineData("""{"bar":{"a":2},"foo":1}""")] + public static void DeserializeGetPropertyCount(string json) + { + var jsonElement = JsonSerializer.Deserialize(json); + Assert.Equal(2, jsonElement.GetPropertyCount()); + } + [Fact] public static void ParseSimpleObject() { using (JsonDocument doc = JsonDocument.Parse(SR.SimpleObjectJson)) { JsonElement parsedObject = doc.RootElement; - int age = parsedObject.GetProperty("age").GetInt32(); string ageString = parsedObject.GetProperty("age").ToString(); string first = parsedObject.GetProperty("first").GetString(); @@ -692,6 +702,9 @@ public static void ParseSimpleObject() Assert.Equal("1 Microsoft Way", street); Assert.Equal("Redmond", city); Assert.Equal(98052, zip); + + var propertyCount = parsedObject.GetPropertyCount(); + Assert.Equal(7, propertyCount); } } @@ -726,6 +739,7 @@ public static void ParseNestedJson() Assert.Equal("Redmond", city); Assert.Equal(98052, zipCode); + Assert.Throws(() => parsedObject.GetPropertyCount()); Assert.Throws(() => person.GetArrayLength()); Assert.Throws(() => phoneNums[2]); Assert.Throws(() => phoneNums.GetProperty("2")); @@ -901,6 +915,7 @@ public static void ReadNumber_1Byte(sbyte value) Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -998,6 +1013,7 @@ public static void ReadNumber_2Bytes(short value) Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1090,6 +1106,7 @@ public static void ReadSmallInteger(int value) Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1193,6 +1210,7 @@ public static void ReadMediumInteger(long value) Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1266,6 +1284,7 @@ public static void ReadLargeInteger(ulong value) Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1339,6 +1358,7 @@ public static void ReadTooLargeInteger() Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1378,6 +1398,7 @@ public static void ReadDateTimeAndDateTimeOffset(string jsonString, string expec Assert.Throws(() => root.GetUInt32()); Assert.Throws(() => root.GetInt64()); Assert.Throws(() => root.GetUInt64()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1461,6 +1482,7 @@ public static void ReadGuid(string jsonString, string expectedStr) Assert.Throws(() => root.GetUInt32()); Assert.Throws(() => root.GetInt64()); Assert.Throws(() => root.GetUInt64()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1563,6 +1585,7 @@ public static void ReadNonInteger(string str, double expectedDouble, float expec Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1653,6 +1676,7 @@ public static void ReadTooPreciseDouble() Assert.Throws(() => root.GetDateTime()); Assert.Throws(() => root.GetDateTimeOffset()); Assert.Throws(() => root.GetGuid()); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1709,6 +1733,7 @@ public static void ReadArrayWithComments() Assert.Throws(() => root.GetUInt64()); Assert.Throws(() => root.TryGetUInt64(out ulong _)); Assert.Throws(() => root.GetString()); + Assert.Throws(() => root.GetPropertyCount()); const string ThrowsAnyway = "throws-anyway"; Assert.Throws(() => root.ValueEquals(ThrowsAnyway)); Assert.Throws(() => root.ValueEquals(ThrowsAnyway.AsSpan())); @@ -1734,6 +1759,7 @@ public static void CheckUseAfterDispose() doc.Dispose(); Assert.Throws(() => root.ValueKind); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); @@ -1792,6 +1818,7 @@ public static void CheckUseDefault() Assert.Equal(JsonValueKind.Undefined, root.ValueKind); + Assert.Throws(() => root.GetPropertyCount()); Assert.Throws(() => root.GetArrayLength()); Assert.Throws(() => root.EnumerateArray()); Assert.Throws(() => root.EnumerateObject()); 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 f689cf296ead17..e5e8b6822b80b9 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 @@ -225,6 +225,7 @@ void VerifyElement(int index) 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.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()); @@ -379,7 +380,7 @@ static IEnumerable GenerateLargeJsonObjectAsFragments(long minLength) public interface ITypeDeserializedFromLargeJson { int FirstValue { get; } - int LastValue { get; } + int LastValue { get; } } public class ClassDeserializedFromLargeJson : ITypeDeserializedFromLargeJson