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

Skip null converters during serialisation to prevent null reference exceptions

Open oughton opened this issue 7 years ago • 9 comments

The converters list in the serialisation settings may contain nulls due to the list being exposed without validation.

Serialisation/deserialisation currently fails with a null reference exception when there is null converter in the list due to the absence of a null guard in converter processing code.

This pull request introduces a check that skips null objects in the converters list during serialisation.

This fixes the following unhelpful exception:

System.NullReferenceException : Object reference not set to an instance of an object. at Newtonsoft.Json.JsonSerializer.GetMatchingConverter(IList`1 converters, Type objectType) at Newtonsoft.Json.JsonSerializer.GetMatchingConverter(Type type) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.GetConverter(JsonContract contract, JsonConverter memberConverter, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at Newtonsoft.Json.Tests.Serialization.JsonSerializerTest.DeserializeWithNullJsonConverter() in D:\tmp\ExternalSources\Newtonsoft.Json\Src\Newtonsoft.Json.Tests\Serialization\JsonSerializerTest.cs:line 3726

oughton avatar Apr 15 '18 16:04 oughton

We are encountering this, but are puzzled why a null converter would end up in the list, any ideas?

CumpsD avatar Feb 20 '20 10:02 CumpsD

We are encountering this, but are puzzled why a null converter would end up in the list, any ideas?

For us it was us populating the converters list in a thread unsafe way. The list is not thread safe so this can lead to nulls in the converters list and the above exception.

oughton avatar Feb 20 '20 13:02 oughton

Likewise... https://github.com/Informatievlaanderen/grar-common/commit/c0406e17a0cb68f7f2dfab0b58a4ee25905409d2

Seems it would be a nice addition if this was threadsafe, it bit us today :)

CumpsD avatar Feb 20 '20 13:02 CumpsD

Hi,

I am getting this error even after upgrading to Newtonsoft.json '13.0.1' version in .Net Framework 4.7.2 Web API.

Occurrence of this error is random, sometime twice a day and sometime twice a week. After recycle application pool or waiting for sometime, this automatically get fixed, so its hard to reproduce.

Below are the details:

An error has occurred. The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'. System.InvalidOperationException An error has occurred. Object reference not set to an instance of an object. System.NullReferenceException at Newtonsoft.Json.JsonSerializer.GetMatchingConverter(IList`1 converters, Type objectType) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty) at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType) at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType) at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding) at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding) at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Tracing.ITraceWriterExtensions.d__19.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.d__1b.MoveNext()

sharmaerish avatar Jun 10 '21 13:06 sharmaerish

@sharmaerish How/where are you adding your custom converters?

oughton avatar Jun 10 '21 13:06 oughton

@oughton thanks for reply.

API which returns the above error are not using any custom converter.

Although, we have used custom converter in our further APIs call. I am attaching my custom Converter for your reference.

We are using custom filter in API constructor, below is the example of API:

[RoutePrefix("Filter")] public class FilterController : ApiController {

    public FilterController()

CustomConverter.txt

    {
        **GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new FilterGroupDataConverter());
        GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new GridSortingInfoDataConverter());**
    }

}

sharmaerish avatar Jun 10 '21 14:06 sharmaerish

@sharmaerish you cannot use GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new GridSortingInfoDataConverter()); in the constructor of an API controller. Add the converters instead in the application startup code.

The reason is because,

  • Each request thread using the same API controller constructs fresh instances of the relevant controller class.
  • This means multiple constructors of the same controller may be executing in parallel on different threads.
  • JsonFormatter.SerializerSettings.Converters list is not thread safe, so multiple threads adding to converters at the same time will lead to inconsistent state of the converts.
  • This can lead to null being in the converters which results in the error you're seeing.

See: https://stackoverflow.com/questions/46890368/web-api-the-createjsonserializer-method-threw-an-exception-when-attempting-to/49812831#49812831

oughton avatar Jun 10 '21 14:06 oughton

@oughton

So this problem occurs because of adding customer converter in JsonFormatter.SerializerSettings.Converters?

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(n

sharmaerish avatar Jun 10 '21 15:06 sharmaerish

@sharmaerish Yes if the converter is added in a thread unsafe way (e.g., by adding in the constructor of an API controller).

oughton avatar Jun 11 '21 08:06 oughton