[API Proposal]: Add Advance method on Utf8JsonReader
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
Research direction
Start by reviewing the existing Utf8JsonReader API and the JsonSerializer.Deserialize(ref reader) usage shown in the proposal. Compare the single-target JsonPath and JsonPathReader alternatives, including behavior for multi-target paths. Done requires an agreed API shape and semantics; the issue does not name implementation files or tests.
Written by the indexing model from the issue text.
Description
Background and motivation
In certain circumstances you may want to partially deserialize a json byte[] or pull out a slice of the json byte[]. If you know the object you can write a routine that can use Utf8JsonReader.Read to advance through the json structure until you get the spot you need but it would be nice if this was a generic routine.
I would imagine we would have to adopt a specific format to indicate where in the json you want to advance to and JsonPath seems like a good candidate. The main drawbacks to this is this Advance method implies a single item you are finding so JsonPaths like $.x.y[?(@.z<10)] wouldn't necessarily work. Single target JsonPaths would work though like $.x.y[0].z.
API Proposal
The API would be a new method / extension on Utf8JsonReader like this.
public ref struct Utf8JsonReader
{
public bool Advance(string jsonPath)
}
We could have overloads that take ReadOnlySpan since this encoding to utf8 bytes will need to be done regardless to efficiently compare property name value spans between the JsonPath and the Utf8JsonReader. We could also consider a new JsonPathReader as an overload.
public ref struct JsonPathReader
{
public bool Read();
public JsonPathToken Current { get; }
}
public ref struct JsonPathToken
{
public int TokenStartIndex { get; }
public JsonPathTokenType TokenType { get; }
public ReadOnlySpan<byte> ValueSpan { get; }
public JsonPathToken(JsonPathTokenType tokenType, int tokenStartIndex, ReadOnlySpan<byte> valueSpan = default)
}
API Usage
Simple usage would be
byte[] json = GetJson();
Utf8JsonReader reader = new(json);
reader.Advance("$.x.y");
// reader is now at the PropertyName token of y
The Advance method would have no opinion on what the user can / should do after it advances but some usages would be.
Get sub json to partial deserialize
byte[] json = GetJson();
Utf8JsonReader reader = new(json);
reader.Advance("$.x.y");
// skip the PropertyName token for y
reader.Read();
// I can now partially deserialize this value only
MyModel model = JsonSerializer.Deserialize<MyModel>(ref reader);
Get the raw bytes of the json represented by the jsonPath
byte[] json = GetJson();
Utf8JsonReader reader = new(json);
reader.Advance("$.x.y");
// skip the PropertyName token for y
reader.Read();
long start = jsonReader.TokenStartIndex;
reader.Skip();
long end = jsonReader.BytesConsumed;
// I can now do things like send these raw bytes to a stream without ever needing to instantiate any objects
ReadOnlySpan<byte> partialJson = json.AsSpan((int)start, (int)(end - start));
Remove an item from a json byte array
byte[] json = GetJson();
Utf8JsonReader reader = new(json);
reader.Advance("$.x.y");
long endLeft = jsonReader.TokenStartIndex;
reader.Skip();
reader.Read();
long startRight = jsonReader.TokenStartIndex;
byte[] newJson = [.. json.AsSpan(0, (int)endLeft), .. json.AsSpan((int)startRight)];
There are many more scenarios these are just a couple that my projects have.
Alternative Designs
I think the tricky part here is the JsonPath. We can hide the implementation of JsonPathReader by just taking in string, byte[], ReadOnlySpan which might allow for some flexibility on implementation in the future.
Not sure if JsonPath is the right format to use given the fact that Advance is intended to work with single target JsonPath's but it is a well known format and it should be easy enough to convey in documentation / summary xml docs that its intended to work with single target only and if a multi-target is passed in it will stop on the first one and if you wanted to iterate through them all you could do.
byte[] json = GetJson();
Utf8JsonReader reader = new(json);
JsonPathReader pathReader = new("$.x.y[?(@.z<10)]");
while(reader.Advance(ref pathReader))
{
//do something with each occurrence
}
An alternative to a new Advance method would be an overload to Read which took the JsonPath, but I feel like this makes the concept of Read more confusing.
public ref struct Utf8JsonReader
{
public bool Read(string jsonPath)
}
Risks
No response
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·