Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue with this approach is that needs to perform a traversal of the entire object to compute the size. This is already possible with EnumerateObject(), we should only include this API if we can guarantee that it's constant-time.

Were you able to determine what the row.SizeOrLength value represents in the case of objects and if so, can be meaningfully used (or changed) so that we can obtain the property count?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some test cases failed when I tried to update the SizeOrLength for object properties count, so I tried this way.
Would try to update the SizeOrLength to do some research on the failed cases, thanks for the input

{
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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<JsonElement>(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();
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -726,6 +739,7 @@ public static void ParseNestedJson()
Assert.Equal("Redmond", city);
Assert.Equal(98052, zipCode);

Assert.Throws<InvalidOperationException>(() => parsedObject.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => person.GetArrayLength());
Assert.Throws<IndexOutOfRangeException>(() => phoneNums[2]);
Assert.Throws<InvalidOperationException>(() => phoneNums.GetProperty("2"));
Expand Down Expand Up @@ -901,6 +915,7 @@ public static void ReadNumber_1Byte(sbyte value)
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -998,6 +1013,7 @@ public static void ReadNumber_2Bytes(short value)
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1090,6 +1106,7 @@ public static void ReadSmallInteger(int value)
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1193,6 +1210,7 @@ public static void ReadMediumInteger(long value)
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1266,6 +1284,7 @@ public static void ReadLargeInteger(ulong value)
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1339,6 +1358,7 @@ public static void ReadTooLargeInteger()
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1378,6 +1398,7 @@ public static void ReadDateTimeAndDateTimeOffset(string jsonString, string expec
Assert.Throws<InvalidOperationException>(() => root.GetUInt32());
Assert.Throws<InvalidOperationException>(() => root.GetInt64());
Assert.Throws<InvalidOperationException>(() => root.GetUInt64());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1461,6 +1482,7 @@ public static void ReadGuid(string jsonString, string expectedStr)
Assert.Throws<InvalidOperationException>(() => root.GetUInt32());
Assert.Throws<InvalidOperationException>(() => root.GetInt64());
Assert.Throws<InvalidOperationException>(() => root.GetUInt64());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1563,6 +1585,7 @@ public static void ReadNonInteger(string str, double expectedDouble, float expec
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1653,6 +1676,7 @@ public static void ReadTooPreciseDouble()
Assert.Throws<InvalidOperationException>(() => root.GetDateTime());
Assert.Throws<InvalidOperationException>(() => root.GetDateTimeOffset());
Assert.Throws<InvalidOperationException>(() => root.GetGuid());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1709,6 +1733,7 @@ public static void ReadArrayWithComments()
Assert.Throws<InvalidOperationException>(() => root.GetUInt64());
Assert.Throws<InvalidOperationException>(() => root.TryGetUInt64(out ulong _));
Assert.Throws<InvalidOperationException>(() => root.GetString());
Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
const string ThrowsAnyway = "throws-anyway";
Assert.Throws<InvalidOperationException>(() => root.ValueEquals(ThrowsAnyway));
Assert.Throws<InvalidOperationException>(() => root.ValueEquals(ThrowsAnyway.AsSpan()));
Expand All @@ -1734,6 +1759,7 @@ public static void CheckUseAfterDispose()
doc.Dispose();

Assert.Throws<ObjectDisposedException>(() => root.ValueKind);
Assert.Throws<ObjectDisposedException>(() => root.GetPropertyCount());
Assert.Throws<ObjectDisposedException>(() => root.GetArrayLength());
Assert.Throws<ObjectDisposedException>(() => root.EnumerateArray());
Assert.Throws<ObjectDisposedException>(() => root.EnumerateObject());
Expand Down Expand Up @@ -1792,6 +1818,7 @@ public static void CheckUseDefault()

Assert.Equal(JsonValueKind.Undefined, root.ValueKind);

Assert.Throws<InvalidOperationException>(() => root.GetPropertyCount());
Assert.Throws<InvalidOperationException>(() => root.GetArrayLength());
Assert.Throws<InvalidOperationException>(() => root.EnumerateArray());
Assert.Throws<InvalidOperationException>(() => root.EnumerateObject());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -379,7 +380,7 @@ static IEnumerable<byte[]> GenerateLargeJsonObjectAsFragments(long minLength)
public interface ITypeDeserializedFromLargeJson
{
int FirstValue { get; }
int LastValue { get; }
int LastValue { get; }
}

public class ClassDeserializedFromLargeJson : ITypeDeserializedFromLargeJson
Expand Down