Newtonsoft.Json icon indicating copy to clipboard operation
Newtonsoft.Json copied to clipboard

DeserializeObject<Object> with empty string returns unexpected null

Open RonenGlants opened this issue 4 years ago • 4 comments

Source/destination types

public class TestsEmptyString
    {
        public string str { get; set; }
    }

Source/destination JSON

"\"\""

Expected behavior

var str = """"; var test = JsonConvert.DeserializeObject<TestsEmptyString>(str); Should throw an exception / go to a custom TypeConverter if there is any. In my case, I have a TypeConverter that takes the string and creates TestsEmptyString object with str set as the string. This works every time except in the case of """". For example ""foo"" gets to my TypeConverter ConvertFrom and CanConvertFrom methods But """" just returns null without getting to that converter.

Please note that any other string like ""foo"" will behave differently than """". Also, my project is configured with nullable annotation context enabled which makes TestsEmptyString not nullable, so I don't understand how it even works.

Actual behavior

returns null to a non-nullble object

Steps to reproduce

var str = "\"\"";
var test = JsonConvert.DeserializeObject<TestsEmptyString>(str); // returns null

var otherStr = "\"foo\"";
var test = JsonConvert.DeserializeObject<TestsEmptyString>(otherStr); // throws an exception

RonenGlants avatar May 05 '20 20:05 RonenGlants

I would like to stress one point here - since C# we have nullable reference types in the language. If the assembly calling to Newtonsoft has opted in to nullable aware context, then 'null' is no longer a legitimate value for a string type from the developers' perspective.

Thus the empty string coersing behavior of Newtosoft.Json performed by CoerceEmptyStringToNull() method (as mentioned in #1687) is seriously wrong as the one that makes C# string not round-tripable, if the nullable annotation context is enabled.

levroz avatar May 06 '20 07:05 levroz

I have a similar problem. I have a type with required fields. I'd want to rely on json serializer to throw serialization exception if it can't correctly deserialize required fields, but an empty string doesn't trigger serialization exception (be it just an empty string "" or an emtpy json string "\"\"");

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Converters;

public class ApiResponse<T>
{
    [JsonProperty(Required = Required.Always)]
    public bool Ok { get; set; }

    [JsonProperty(Required = Required.Always)]
    public T Result { get; set; }
}

var emptyJsonString = "\"\"";
JsonConvert.DeserializeObject<ApiResponse<int>>(emptyJsonString)  // returns null

var emptyString = "";
JsonConvert.DeserializeObject<ApiResponse<int>>(emptyString)      // returns null

tuscen avatar Jul 03 '20 18:07 tuscen

System.Text.Json work like expected and when trying to de-serialize empty string will (always?) fail: System.Text.Json.JsonException: 'The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.'

Same is true for all: Newtonsoft.Json.JsonConvert.DeserializeObject(""); // null System.Text.Json.JsonSerializer.Deserialize(""); // error

Newtonsoft.Json.JsonConvert.DeserializeObject(""); // null System.Text.Json.JsonSerializer.Deserialize(""); // error

Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(""); // null System.Text.Json.JsonSerializer.Deserialize<MyClass>(""); // error

osexpert avatar May 26 '21 12:05 osexpert

Hello! This is still actual. It is very ambiguous because missing required property is treated as an Exception, however, something like this " " return null. I understand, that just starting to throw exceptions, in this case, may break backward compatibility, but could you please consider adding an option to JsonSerializerSettings which would enable throwing Exceptions in such cases?

xelmed avatar Jul 12 '22 09:07 xelmed