-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Ensure that JSON properties can be skipped without buffering their entire contents. #96856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,7 +106,7 @@ public readonly int CurrentDepth | |
get | ||
{ | ||
int readerDepth = _bitStack.CurrentDepth; | ||
if (TokenType == JsonTokenType.StartArray || TokenType == JsonTokenType.StartObject) | ||
if (TokenType is JsonTokenType.StartArray or JsonTokenType.StartObject) | ||
{ | ||
Debug.Assert(readerDepth >= 1); | ||
readerDepth--; | ||
|
@@ -115,7 +115,7 @@ public readonly int CurrentDepth | |
} | ||
} | ||
|
||
internal bool IsInArray => !_inObject; | ||
internal readonly bool IsInArray => !_inObject; | ||
|
||
/// <summary> | ||
/// Gets the type of the last processed JSON token in the UTF-8 encoded JSON text. | ||
|
@@ -322,15 +322,15 @@ private void SkipHelper() | |
{ | ||
Debug.Assert(_isFinalBlock); | ||
|
||
if (TokenType == JsonTokenType.PropertyName) | ||
if (TokenType is JsonTokenType.PropertyName) | ||
{ | ||
bool result = Read(); | ||
// Since _isFinalBlock == true here, and the JSON token is not a primitive value or comment. | ||
// Read() is guaranteed to return true OR throw for invalid/incomplete data. | ||
Debug.Assert(result); | ||
} | ||
|
||
if (TokenType == JsonTokenType.StartObject || TokenType == JsonTokenType.StartArray) | ||
if (TokenType is JsonTokenType.StartObject or JsonTokenType.StartArray) | ||
{ | ||
int depth = CurrentDepth; | ||
do | ||
|
@@ -375,41 +375,58 @@ public bool TrySkip() | |
return true; | ||
} | ||
|
||
return TrySkipHelper(); | ||
Utf8JsonReader restore = this; | ||
bool success = TrySkipPartial(targetDepth: CurrentDepth); | ||
if (!success) | ||
{ | ||
// Roll back the reader if it contains partial data. | ||
this = restore; | ||
} | ||
|
||
return success; | ||
} | ||
|
||
private bool TrySkipHelper() | ||
/// <summary> | ||
/// Tries to skip the children of the current JSON token, advancing the reader even if there is not enough data. | ||
/// The skip operation can be resumed later, provided that the same <paramref name="targetDepth" /> is passed. | ||
/// </summary> | ||
/// <param name="targetDepth">The target depth we want to eventually skip to.</param> | ||
/// <returns>True if the entire JSON value has been skipped.</returns> | ||
internal bool TrySkipPartial(int targetDepth) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existing |
||
{ | ||
Debug.Assert(!_isFinalBlock); | ||
|
||
Utf8JsonReader restore = this; | ||
Debug.Assert(0 <= targetDepth && targetDepth <= CurrentDepth); | ||
|
||
if (TokenType == JsonTokenType.PropertyName) | ||
if (targetDepth == CurrentDepth) | ||
{ | ||
if (!Read()) | ||
// This is the first call to TrySkipHelper. | ||
if (TokenType is JsonTokenType.PropertyName) | ||
{ | ||
goto Restore; | ||
// Skip any property name tokens preceding the value. | ||
if (!Read()) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
if (TokenType is not (JsonTokenType.StartObject or JsonTokenType.StartArray)) | ||
{ | ||
// The next value is not an object or array, so there is nothing to skip. | ||
return true; | ||
} | ||
} | ||
|
||
if (TokenType == JsonTokenType.StartObject || TokenType == JsonTokenType.StartArray) | ||
// Start or resume iterating through the JSON object or array. | ||
do | ||
{ | ||
int depth = CurrentDepth; | ||
do | ||
if (!Read()) | ||
{ | ||
if (!Read()) | ||
{ | ||
goto Restore; | ||
} | ||
return false; | ||
} | ||
while (depth < CurrentDepth); | ||
} | ||
while (targetDepth < CurrentDepth); | ||
|
||
Debug.Assert(targetDepth == CurrentDepth); | ||
return true; | ||
|
||
Restore: | ||
this = restore; | ||
return false; | ||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -230,14 +230,14 @@ internal sealed override bool OnTryRead( | |
{ | ||
if (state.Current.PropertyState == StackFramePropertyState.None) | ||
{ | ||
state.Current.PropertyState = StackFramePropertyState.ReadName; | ||
|
||
// Read the key name. | ||
if (!reader.Read()) | ||
{ | ||
value = default; | ||
return false; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing the order of state update operations since we no longer rely on the root method to advance the reader for us on resumed operations. |
||
state.Current.PropertyState = StackFramePropertyState.ReadName; | ||
} | ||
|
||
// Determine the property. | ||
|
@@ -274,14 +274,14 @@ internal sealed override bool OnTryRead( | |
|
||
if (state.Current.PropertyState < StackFramePropertyState.ReadValue) | ||
{ | ||
state.Current.PropertyState = StackFramePropertyState.ReadValue; | ||
|
||
if (!SingleValueReadWithReadAhead(_valueConverter.RequiresReadAhead, ref reader, ref state)) | ||
if (!reader.TryAdvanceWithOptionalReadAhead(_valueConverter.RequiresReadAhead)) | ||
{ | ||
state.Current.DictionaryKey = key; | ||
value = default; | ||
return false; | ||
} | ||
|
||
state.Current.PropertyState = StackFramePropertyState.ReadValue; | ||
} | ||
|
||
if (state.Current.PropertyState < StackFramePropertyState.TryRead) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic moved essentially unchanged from
JsonConverter.ReadAhead.cs