-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Breaking changes for JsonSerializer.Deserialize #20956
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
Conversation
|
|
||
| In previous .NET versions, when the type parameter is <xref:System.Char>, passing a null reference for the <xref:System.String> parameter of <xref:System.Text.Json.JsonSerializer.Deserialize%60%601(System.String,System.Text.Json.JsonSerializerOptions)?displayProperty=nameWithType> causes a <xref:System.NullReferenceException> to be thrown. Passing an empty string causes an <xref:System.IndexOutOfRangeException> to be thrown. | ||
|
|
||
| In .NET Core 3.1 and later, when the type parameter is <xref:System.Char>, passing a null reference or an empty string causes a <xref:System.Text.Json.JsonException> to be thrown. |
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.
NIT: I think adding the example code might make it more clear, up to you
// Before (3.0): throws NullReferenceException
// After (3.1): throw JsonException
JsonSerializer.Deserialize<char>("null");
// Before (3.0): throws IndexOutOfRangeException
// After (3.1): throw JsonException
JsonSerializer.Deserialize<char>("");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.
Okay, I will add it. But isn't "null" just a string that contains the characters n, u, l, and l?
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.
Good question, it might supposed to be JsonSerializer.Deserialize<char>(null);, but also "null" string might be serialized into null value instead of "null" string cc @layomia @ahsonkhan
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.
Okay, I will add it. But isn't
"null"just a string that contains the characters n, u, l, and l?
Yes, that is true. That example input string will showcase the change in behavior going from NullReferenceException to JsonException between 3.0 and 3.1.
I responded to this question in more detail below:
#20956 (comment)
it might supposed to be
JsonSerializer.Deserialize<char>(null);,
The following snippet is invalid (it will fail to compile) and once fixed, it will throw an ArgumentNullException (which is the desired behavior and that hasn't changed since 3.0 to 5.0):
JsonSerializer.Deserialize<char>((string)null);Another example that showcases the change in behavior is a null string in quotes, i.e. the six characters ", n, u, l, l, ".
...des/core-changes/serialization/5.0/deserializing-json-into-char-requires-single-character.md
Show resolved
Hide resolved
buyaa-n
left a comment
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.
Left a NIT comment, overall looks good, thanks!
| JsonSerializer.Deserialize<char>("abc"); | ||
|
|
||
| // Correct usage. | ||
| JsonSerializer.Deserialize<char>("a"); |
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.
Both these are invalid JSON because they are missing the string within embedded quotes, These should be:
| JsonSerializer.Deserialize<char>("abc"); | |
| // Correct usage. | |
| JsonSerializer.Deserialize<char>("a"); | |
| JsonSerializer.Deserialize<char>("\"abc\""); | |
| // Correct usage. | |
| JsonSerializer.Deserialize<char>("\"a\""); |
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.
But then the first character is always ", so how do you pass a single-character string?
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.
That is how you pass a single character string.
Here's an example valid, single-character JSON string:
"a"
In JSON, a string token is one that is surrounded with quotes.
Another valid JSON token, is a number (which isn't in quotes), so for example:
1
The way you would write that particular JSON number token in C# is as follows (note, there are no extra quotes):
JsonSerializer.Deserialize<int>("1");More commonly, folks have JSON objects, like this:
JsonSerializer.Deserialize<SomeClass>("{ \"name\": \"value\", \"age\": 5 }");https://www.json.org/json-en.html
Maybe if you think of it as a character array rather than strings, that might clear up the need for the quotes:
byte[] arr = {(byte)'"', (byte)'a', (byte)'"'};
Console.WriteLine(JsonSerializer.Deserialize<char>(arr.AsSpan())); // valid, single character 'a'
byte[] another = {(byte)'1', (byte)'2', (byte)'3'};
Console.WriteLine(JsonSerializer.Deserialize<int>(another.AsSpan())); // valid, integer number 123There 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.
You can explore various values in jsonlint to see what is valid and invalid JSON payloads:
https://jsonlint.com/
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.
Both these are invalid JSON because they are missing the string within embedded quotes, These should be:
Ah, forgot about that
|
|
||
| // .NET Core 3.0: Throws IndexOutOfRangeException. | ||
| // .NET Core 3.1 and later: Throws JsonException. | ||
| JsonSerializer.Deserialize<char>(""); |
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.
| JsonSerializer.Deserialize<char>(""); | |
| JsonSerializer.Deserialize<char>("\"\""); |
includes/core-changes/serialization/3.1/jsonserializer-deserialize-throws-jsonexception.md
Outdated
Show resolved
Hide resolved
|
@buyaa-n - can you please also update the examples in the breaking change issue #20815 (comment) to match the quoted string fixes I suggested above.
Edit: Nevermind, I see the separate issue for that: #20823 |
|
Also, to clarify, there is only a single truly breaking change that needs to be documented (between version 3.1 and 5.0): // This used to work "incorrectly" before, and now it throws an exception.
JsonSerializer.Deserialize<char>("\"abc\"");Between 3.0 and 3.1 nothing needs to be documented as "breaking" (at least in this JSON deserialization scenario). The other examples are changing one exception type to another and those are not considered breaking. In fact, going from NullRefException to a more user-friendly exception is encouraged. using System;
using System.Text.Json;
namespace TestNull
{
public class Program
{
public class MyPoco
{
public char Character { get; set; }
}
static void Main(string[] args)
{
// === Changing exception types like this is NOT considered a breaking change, no doc needed. ===
// Before (3.0): NullReferenceException
// After (3.1): JsonException
// After (5.0): JsonException
JsonSerializer.Deserialize<MyPoco>("{\"Character\": null}");
// === Changing exception types like this is NOT considered a breaking change doc, no doc needed. ===
// Before (3.0): NullReferenceException
// After (3.1): JsonException
// After (5.0): JsonException
JsonSerializer.Deserialize<MyPoco>("{\"Character\": \"null\"}");
// === Changing exception types like this is NOT considered a breaking change doc, no doc needed. ===
// Before (3.0): IndexOutOfRangeException
// After (3.1): JsonException
// After (5.0): JsonException
JsonSerializer.Deserialize<MyPoco>("{\"Character\": \"\"}");
// === This is the ONLY change that needs to be documented as it is the only one that is breaking. ===
// Before (3.0): Set Character to the first character - 'a'
// Before (3.1): Set Character to the first character - 'a'
// After (5.0): JsonException
var val = JsonSerializer.Deserialize<MyPoco>("{\"Character\": \"abc\"}");
Console.WriteLine(val.Character);
}
}
} |
Whoops, they were within the examples of your breaking change comment so thought all of them breaking 😛, but you are right, I will update the issue accordingly, @gewarren let's remove the NRE, IOOR exceptions section from the doc, sorry for the confusion 🙏 |
|
@gewarren the issues now update as needed |
buyaa-n
left a comment
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.
Thank you @gewarren looks great

Fixes #20815.
Preview link - 5.0.