Skip to content

SDK 6 Throws NullReferenceException when serializing with a null list member #3329

@almostchristian

Description

@almostchristian

Describe the bug

Our developers sometimes adds null Extension objects inside Extensions list.

var patient = new Patient { Id = "example" };
patient.Extension = [null, new("http://test", new FhirString("abcd"))];

// Expected json: {"resourceType": "Patient", "id": "example", "extension": [{"url":"http://test", "valueString": "abcd"}]}

// Wrong json: {"resourceType": "Patient", "id": "example", "extension": [null, {"url":"http://test", "valueString": "abcd"}]}

When we used the Newtonsoft.Json serializer, this would be automatically be set ignored during serialization, but using the SystemTextJson serializer prior to SDK 6, we added a custom SerializationFilter that would filter out these nulls,

sealed class NullIgnoringSerializationFilter(SerializationFilter? original) : SerializationFilter
{
    public override void EnterObject(object value, ClassMapping? mapping)
        => original?.EnterObject(value, mapping);

    public override void LeaveMember(string name, object value, PropertyMapping? mapping)
        => original?.LeaveMember(name, value, mapping);

    public override void LeaveObject(object value, ClassMapping? mapping)
        => original?.LeaveObject(value, mapping);

    public override bool TryEnterMember(string name, object value, PropertyMapping? mapping)
    {
        if (value is IList collection)
        {
            while (collection.Contains(null))
            {
                collection.Remove(null!);
            }
        }

        return original?.TryEnterMember(name, value, mapping) ?? true;
    }
}

However when tested with SDK6, whenever a null is encountered in a list, this exception always shows up:

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at Hl7.Fhir.Model.BaseExtensions.DeepCopy[T](T source)
   at Hl7.Fhir.Model.BaseExtensions.<>c__8`1.<DeepCopyInternal>b__8_0(T item)
   at System.Linq.Enumerable.SelectListIterator`2.Fill(ReadOnlySpan`1 source, Span`1 destination, Func`2 func)
   at System.Linq.Enumerable.SelectListIterator`2.ToList()
   at Hl7.Fhir.Model.BaseExtensions.DeepCopyInternal[T](IEnumerable`1 source)
   at Hl7.Fhir.Model.DomainResource.CopyToInternal(Base other)
   at Hl7.Fhir.Model.Patient.CopyToInternal(Base other)
   at Hl7.Fhir.Model.Patient.DeepCopyInternal()
   at Hl7.Fhir.Model.BaseExtensions.DeepCopy[T](T source)
   at Hl7.Fhir.Utility.SerializationUtil.MakeSubsettedClone(Base source)
   at Hl7.Fhir.Serialization.BaseFhirJsonSerializer.Serialize(Base instance, Utf8JsonWriter writer, Func`1 filterFactory)
   at Hl7.Fhir.Serialization.FhirJsonConverter`1.Write(Utf8JsonWriter writer, TF poco, JsonSerializerOptions options)
   at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Serialize(Utf8JsonWriter writer, T& rootValue, Object rootValueBoxed)
   at System.Text.Json.JsonSerializer.WriteString[TValue](TValue& value, JsonTypeInfo`1 jsonTypeInfo)
   at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
   at Program.<Main>$(String[] args) in D:\repos\FhirSerializationTest\FhirSerializationTest\Program.cs:line 16

I asked our developers to avoid these null list members in the future. Also, it would be good if the serializer can automatically ignore these null members without having to write a serialization filter, or at least, honor the JsonSerializerOptions.DefaultIgnoreCondition when it is set to JsonIgnoreCondition.WhenWritingDefault.

Versions used:
6.0.1, 5.12.2

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions