-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
System.Text.Json: Utf8JsonWriter support for application/x-json-stream #82314
Comments
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis Issue DetailsGreetings once again STJ team! 😄 I ran into a kind of interesting situation trying to send an The format I want is... {"item1key1": "value1"}\n
{"item2key1": "value1"}\n
{"item3key1": "value1"}\n Essentially newline delimited JSON. I made it work by doing this (more or less)... var stream = new MemoryStream();
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { SkipValidation = true });
foreach (var item in batch)
{
// This is the interesting line here.
writer.Reset(stream);
this.SerializeItemToJson(item, writer);
writer.Flush();
stream.Write(NewLine, 0, 1);
} The interesting line is the The challenge I have is the What I would like is either a way to just reset the depth so the comma isn't emitted or a way to tell Thoughts?
|
I'll let the dotnet team answer officially, but my guess is that the writer wasn't designed to write multiple JSON instances (as you do with JSON streams). It's probably expected that you create a new writer for a new instance (you can likely keep the same stream, though). I like your solution, though. |
Related issue for the reading side: #33030. |
What does the |
This issue has been marked |
@eiriktsarpalis The bummer with the current design is I have to call it in my inner loop. So I wouldn't say it is a slow operation, but it is slower than needed. Here's a benchmark:
My particular use case is I have a batch of items in memory I need to send off at some interval or when it gets full. Benchmark codepublic class Utf8JsonWriterBenchmarks
{
private static readonly byte[] NewLine = "\n"u8.ToArray();
private static readonly JsonEncodedText IndexPropertyName = JsonEncodedText.Encode("Index");
private static readonly JsonEncodedText NamePropertyName = JsonEncodedText.Encode("Name");
private static readonly Action<Utf8JsonWriter> ResetDepth = BuildResetDepthAction();
private readonly MemoryStream stream = new();
private readonly Utf8JsonWriter writer;
public Utf8JsonWriterBenchmarks()
{
this.writer = new(this.stream, new JsonWriterOptions { SkipValidation = true });
}
[Params(1, 100, 1000)]
public int NumberOfItems { get; set; }
[Benchmark]
public void ResetBenchmark()
{
this.stream.Position = 0;
for (int i = 0; i < this.NumberOfItems; i++)
{
this.writer.Reset(this.stream);
SerializeItem(i, this.writer);
this.writer.Flush();
this.stream.Write(NewLine, 0, 1);
}
}
[Benchmark]
public void ResetDepthBenchmark()
{
this.stream.Position = 0;
this.writer.Reset(this.stream);
for (int i = 0; i < this.NumberOfItems; i++)
{
ResetDepth(this.writer);
SerializeItem(i, this.writer);
this.writer.Flush();
this.stream.Write(NewLine, 0, 1);
}
}
private static void SerializeItem(int index, Utf8JsonWriter writer)
{
writer.WriteStartObject();
writer.WriteNumber(IndexPropertyName, index);
writer.WriteString(NamePropertyName, "name");
writer.WriteEndObject();
}
private static Action<Utf8JsonWriter> BuildResetDepthAction()
{
var currentDepthField = typeof(Utf8JsonWriter).GetField("_currentDepth", BindingFlags.Instance | BindingFlags.NonPublic);
var dynamicMethod = new DynamicMethod(
"ResetDepth",
returnType: null,
new Type[] { typeof(Utf8JsonWriter) },
typeof(Utf8JsonWriterBenchmarks).Module,
skipVisibility: true);
var generator = dynamicMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Stfld, currentDepthField);
generator.Emit(OpCodes.Ret);
return (Action<Utf8JsonWriter>)dynamicMethod.CreateDelegate(typeof(Action<Utf8JsonWriter>));
}
} |
You might be able to avoid a number of checks if you just use the parameterless [Benchmark(Baseline = true)]
public void ResetBenchmark()
{
this.stream.Position = 0;
for (int i = 0; i < this.NumberOfItems; i++)
{
SerializeItem(i, this.writer);
this.writer.Flush();
this.stream.Write(NewLine, 0, 1);
this.writer.Reset();
}
} Which produces slightly better numbers percentage-wise:
Improvement is marginal, at the expense of not fully resetting the state of the writer. I would expect it to be even less pronounced if each loop called into So I do think |
In my actual code I do have to reset to a different stream but... writer.Reset(inputStream);
for (...)
{
// do stuff
writer.Reset();
} ...works for me. Didn't realize the parameter-less reset preserves the current stream. Nice! I will say trying to do this
Probably something could be done to make it better but I'm not blocked so I'll just leave this out there in case anyone else wants to push for it 😄 |
Another downside to needing to reset the writer is that it also requires flushing the writer and by extension, the underlying stream. This causes problems with, e.g., using a As @CodeBlanch said, using I also just want to note that if we had the ability to disable the automatic comma insertion (e.g. a property like var stream = new MemoryStream();
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { SkipValidation = true, SkipAutomaticListSeparatorInsertionAtDepthZero = true });
foreach (var item in batch)
{
this.SerializeItemToJson(item, writer);
writer.WriteRawValue("\n", skipInputValidation: true);
} |
Greetings once again STJ team! 😄
I ran into a kind of interesting situation trying to send an
application/x-json-stream
POST usingUtf8JsonWriter
.The format I want is...
Essentially newline delimited JSON.
I made it work by doing this (more or less)...
The interesting line is the
writer.Reset
. That is needed because of depth tracking insideUtf8JsonWriter
which causes a,
to be written out after the first item.The challenge I have is the
Reset
method does a whole lot of housekeeping which slows things down.What I would like is either a way to just reset the depth so the comma isn't emitted or a way to tell
Utf8JsonWriter
to emit\n
where it would have done the comma (but only for these top-level objects).Thoughts?
The text was updated successfully, but these errors were encountered: