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<byte> utf8PropertyName) { throw null; }
         public System.Text.Json.JsonElement GetProperty(System.ReadOnlySpan<char> 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 a53df982c12eb4..dca61c889c89d8 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 ccf524fc40f3a4..7b3ffa246a90ca 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,7 +950,7 @@ private static void Parse(
             ref StackRowStack stack)
         {
             bool inArray = false;
-            int arrayItemsCount = 0;
+            int arrayItemsOrPropertyCount = 0;
             int numberOfRowsForMembers = 0;
             int numberOfRowsForValues = 0;
 
@@ -972,13 +972,14 @@ private static void Parse(
                 {
                     if (inArray)
                     {
-                        arrayItemsCount++;
+                        arrayItemsOrPropertyCount++;
                     }
 
                     numberOfRowsForValues++;
                     database.Append(tokenType, tokenStart, DbRow.UnknownSize);
-                    var row = new StackRow(numberOfRowsForMembers + 1);
+                    var row = new StackRow(arrayItemsOrPropertyCount, numberOfRowsForMembers + 1);
                     stack.Push(row);
+                    arrayItemsOrPropertyCount = 0;
                     numberOfRowsForMembers = 0;
                 }
                 else if (tokenType == JsonTokenType.EndObject)
@@ -987,7 +988,7 @@ private static void Parse(
 
                     numberOfRowsForValues++;
                     numberOfRowsForMembers++;
-                    database.SetLength(rowIndex, numberOfRowsForMembers);
+                    database.SetLength(rowIndex, arrayItemsOrPropertyCount);
 
                     int newRowIndex = database.Length;
                     database.Append(tokenType, tokenStart, reader.ValueSpan.Length);
@@ -995,20 +996,21 @@ private static void Parse(
                     database.SetNumberOfRows(newRowIndex, numberOfRowsForMembers);
 
                     StackRow row = stack.Pop();
-                    numberOfRowsForMembers += 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)
@@ -1017,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
@@ -1030,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);
                     }
@@ -1040,13 +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++;
+                    arrayItemsOrPropertyCount++;
 
                     // Adding 1 to skip the start quote will never overflow
                     Debug.Assert(tokenStart < int.MaxValue);
@@ -1068,7 +1071,7 @@ private static void Parse(
 
                     if (inArray)
                     {
-                        arrayItemsCount++;
+                        arrayItemsOrPropertyCount++;
                     }
 
                     if (tokenType == JsonTokenType.String)
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()
         /// <exception cref="ObjectDisposedException">
         ///   The parent <see cref="JsonDocument"/> has been disposed.
         /// </exception>
-        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..8be93677470ec5 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<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -999,6 +1002,7 @@ public static void ReadNumber_2Bytes(short value)
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1091,6 +1095,7 @@ public static void ReadSmallInteger(int value)
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1194,6 +1199,7 @@ public static void ReadMediumInteger(long value)
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1267,6 +1273,7 @@ public static void ReadLargeInteger(ulong value)
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1340,6 +1347,7 @@ public static void ReadTooLargeInteger()
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1379,6 +1387,7 @@ public static void ReadDateTimeAndDateTimeOffset(string jsonString, string expec
                 Assert.Throws<InvalidOperationException>(() => root.GetInt64());
                 Assert.Throws<InvalidOperationException>(() => root.GetUInt64());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1462,6 +1471,7 @@ public static void ReadGuid(string jsonString, string expectedStr)
                 Assert.Throws<InvalidOperationException>(() => root.GetInt64());
                 Assert.Throws<InvalidOperationException>(() => root.GetUInt64());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1564,6 +1574,7 @@ public static void ReadNonInteger(string str, double expectedDouble, float expec
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1654,6 +1665,7 @@ public static void ReadTooPreciseDouble()
                 Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
                 Assert.Throws<InvalidOperationException>(() => root.GetGuid());
                 Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+                Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
                 Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
                 Assert.Throws<InvalidOperationException>(() => root.GetBoolean());
@@ -1735,6 +1747,7 @@ public static void CheckUseAfterDispose()
 
                 Assert.Throws<ObjectDisposedException>(() => root.ValueKind);
                 Assert.Throws<ObjectDisposedException>(() => root.GetArrayLength());
+                Assert.Throws<ObjectDisposedException>(() => root.GetPropertyCount());
                 Assert.Throws<ObjectDisposedException>(() => root.EnumerateArray());
                 Assert.Throws<ObjectDisposedException>(() => root.EnumerateObject());
                 Assert.Throws<ObjectDisposedException>(() => root.GetDouble());
@@ -1793,6 +1806,7 @@ public static void CheckUseDefault()
             Assert.Equal(JsonValueKind.Undefined, root.ValueKind);
 
             Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
+            Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
             Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
             Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
             Assert.Throws<InvalidOperationException>(() => 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,46 @@ public static void ParseValue_AllowMultipleValues_TrailingContent()
             JsonTestHelper.AssertThrows<JsonException>(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<JsonElement>(json);
+            Assert.Equal(expectedCount, element.GetPropertyCount());
+        }
+
+        [Fact]
+        public static void VerifyGetPropertyCountAndArrayLengthUsingEnumerateMethods()
+        {
+            using (JsonDocument doc = JsonDocument.Parse(SR.ProjectLockJson))
+            {
+                CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(doc.RootElement);
+            }
+
+            void CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(JsonElement elem)
+            {
+                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) 
+                {
+                    Assert.Equal(elem.EnumerateArray().Count(), elem.GetArrayLength());
+                    foreach (JsonElement item in elem.EnumerateArray())
+                    {
+                        CheckPropertyCountAndArrayLengthAgainstEnumerateMethods(item);
+                    }
+                }
+            }
+        }
+
         [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 f689cf296ead17..e296ee97c8d068 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());
