Is the Json.NET JsonSerializer threadsafe?
Asked Answered
C

5

47

I'm trying to reduce the amount of garbage my web service generates, and I noticed that we're creating a new Json.NET JsonSerializer instance for each request. It is not the most lightweight object ever, so I'm wondering if I can just create a single instance and reuse it for all requests. Primarily this requires that it be threadsafe during serialization and deserialization.

The documentation doesn't say whether it's threadsafe or not.

Inspecting the code it appears that the serialization and deserialization methods are threadsafe, as long as you don't change any settings on the object at the same time. However, it's a complex class so I'm not 100% sure of my analysis.

Has anyone tried reusing instances of JsonSerializer and had it work or not? Are there any known problems with reusing it?

Chinfest answered 23/3, 2016 at 18:42 Comment(2)
how about creating one instance for each thread in the thread local storage?Access
How about using a pool?Gordan
D
57

Inspecting the code it appears that the serialization and deserialization methods are threadsafe, as long as you don't change any settings on the object at the same time.

Correct, JsonSerializer is threadsafe.

No state is shared while serializing but if you change a setting on the JsonSerializer while in the middle of serializing an object then those will automatically be used.

Doubleteam answered 23/3, 2016 at 21:55 Comment(3)
Be careful with Json.NET version, we used the version 7.0.1 and JsonTextReader was not thread safe. After an update to the version 9.0.1 it works fine.Dissimulate
JsonTextReader has state specific to the JSON it is reading and will never be thread safe.Doubleteam
@JamesNewton-King Very late to chime in but would you concur with my answer that JsonSerializer is so cheap to create that doing it on demand should work just fine in the vast majority of scenarios?Luben
C
16

According to the Feature Comparison on the Newtonsoft site, it is thread safe, as are DataContractJsonSerializer and JavaScriptSerializer.

enter image description here

Caustic answered 23/3, 2016 at 18:48 Comment(1)
JSON.NET does contain thread-unsafe classes (obviously), this is just a generic product feature matrix.Kellen
J
8

If you don't use references, JsonSerializer is thread-safe. There are however a few issues when using references in a multi-threaded context.

First, there is a bug in the default ReferenceResolver that may cause duplicate reference id's to be used. See the GitHub issue here.

Secondly, when reusing a JsonSerializer the default ReferenceResolver is stateful so that if you use references your reference ids will continue to increment with each serialization call you make instead of starting at 1 for each. I created a GitHub issue to address this problem here.

Joeljoela answered 27/7, 2017 at 18:28 Comment(2)
Pls clarify - is "the default ReferenceResolver" threadsafe? If not then I guess JsonSerializer is unsafe too?Kellen
I've updated my answer. I also included a reference to a bug in the default ReferenceResolver.Joeljoela
L
1

I noticed that we're creating a new Json.NET JsonSerializer instance for each request. It is not the most lightweight object ever...

Maybe not "ever", but I suspect it's a very inexpensive object to create, because the library itself does this routinely, such as the static and oft-used JsonConvert.SerializeObject method, which is defined like this:

public static string SerializeObject(object value, Type type, JsonSerializerSettings settings)
{
    JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);

    return SerializeObjectInternal(value, type, jsonSerializer);
}

Given the maturity and popularity of the library, I suspect that if JsonSerializer was even a little expensive to create, some effort would have been made to cache them in these static methods. So although it's thread safe, I still think that unless it's the most extreme of circumstances you're going to be fine creating them on demand.

Luben answered 18/4, 2019 at 20:10 Comment(3)
If your service does thousands or 10's of thousands of requests/sec (inbound & outbound) this stuff can matter. And many libraries are optimized for usability rather than performance, so I would not blithely assume that if it mattered for your particular case then the library would've handled it already. That said, using a more efficient protocol (eg, Protobuf) would be an even better option, if possible. Also, IDK why you're going off of suspicions and guesses when the code is easily available.Chinfest
@BrianReischl Of course the code is easily available, which is why I linked to it and even pasted a snippet of it. :) I made no suspicion or guess about the current state of the code; I actually studied it closely before posting. My only speculation was about why they chose not to cache JsonSerializer instances for use by static JsonConvert methods. Difficult to implement? Not at all. No one thought to suggest it? Highly unlikely given its millions of users. So cheap to create that in the vast majority of cases it doesn't really matter? That's the most logical reason in my mind.Luben
I don't at all dispute that "this stuff can matter", but how much so is a function of how "expensive" it is to create and hold many instances of the object in question. I even asked JNK in his answer above if he could confirm my hunches. If I'm wrong, I'll gladly retract my answer. :)Luben
S
0

No. It is not threadsafe. Even in 2024 and with version 13.0.3 it sometimes throws something like this:

Newtonsoft.Json.JsonSerializationException: Error writing object reference for 'maxkpd.app.api.DM.drParam'. Path 'result.Params[18]'. ---> System.ArgumentException: A different value already has the Id '83302'.

Corresponding issue: https://github.com/JamesNK/Newtonsoft.Json/issues/1452

Semitic answered 29/3 at 8:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.