Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Make GetPropertyCount public and fix its return value
  • Loading branch information
etemi committed Aug 15, 2024
commit 45818a8f9466ed83a6ac5e170e292756f0cbac30
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 @@ -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)
Copy link
Member

Choose a reason for hiding this comment

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

I think it might make sense to validate if this comment is consistent with the current implementation, just to make sure we're not accidentally regressing something.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI: For me it seemed that the comment was wrong because previously it wasn't always 1 for StartObject. Instead it was set to the same value as the third int (number of rows until the next value). See first and last line here:

database.SetLength(rowIndex, numberOfRowsForMembers);
int newRowIndex = database.Length;
database.Append(tokenType, tokenStart, reader.ValueSpan.Length);
database.SetNumberOfRows(rowIndex, numberOfRowsForMembers);

Copy link
Member

Choose a reason for hiding this comment

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

Looks like the comment was always wrong (at least, was wrong in the initial merge, maybe it was right when I wrote it during feature development).

Nothing noticed or cared that the value was wrong, because nothing read the value until DeepEquals was added... and I guess it didn't notice that it was wrong, because it was the "same wrong" when two things were equal.

Copy link
Member

@eiriktsarpalis eiriktsarpalis Aug 17, 2024

Choose a reason for hiding this comment

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

I'm trying to decide if this means that we should backport the change to 9. Is it possible for the original values (number of rows) to be equal when the number of properties are not equal? Even if that wouldn't impact functional correctness, it would prompt the comparer to recurse into a more expensive property-wise comparison for differently-sized objects.

Copy link
Contributor Author

@etemi etemi Aug 17, 2024

Choose a reason for hiding this comment

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

It is possible to have equal number of rows when the number of properties are not equal:

  • Example 1

    10 rows and 2 properties:

    { "a": [1,2,3,4], "b": 4 }

    10 rows but 3 properties:

    { "a": [1,2], "b": 3, "c": 4 }
  • Example 2

    7 rows and 1 property:

    { "a": [1,2,3] }

    7 rows and 3 properties:

    { "a": 1, "b": 2, "c": true }

But I would say it is very unlikely that two unequal values accidentally have the same number of rows.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, I think we should try to backport this.

// * 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -977,25 +978,27 @@ 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)
{
int rowIndex = database.FindIndexOfFirstUnsetSizeOrLength(JsonTokenType.StartObject);

numberOfRowsForValues++;
numberOfRowsForMembers++;
database.SetLength(rowIndex, numberOfRowsForMembers);
database.SetLength(rowIndex, numberOfProperties);

int newRowIndex = database.Length;
database.Append(tokenType, tokenStart, reader.ValueSpan.Length);
database.SetNumberOfRows(rowIndex, numberOfRowsForMembers);
database.SetNumberOfRows(newRowIndex, numberOfRowsForMembers);

StackRow row = stack.Pop();
numberOfRowsForMembers += row.SizeOrLength;
numberOfRowsForMembers += row.NumberOfRows;
numberOfProperties = row.SizeOrLength;
}
else if (tokenType == JsonTokenType.StartArray)
{
Expand Down Expand Up @@ -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);
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 @@ -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());

Expand All @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -2442,13 +2456,15 @@ 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;

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]);
Expand Down Expand Up @@ -3437,6 +3453,16 @@ 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 EnsureResizeSucceeds()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down