Skip to content
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

Deserialization of Bundle.EntryComponent with a resource causes exception System.InvalidOperationException: 'Stack empty.' #2627

Closed
cames-be opened this issue Nov 17, 2023 · 2 comments · Fixed by #2731
Assignees

Comments

@cames-be
Copy link

Describe the bug
A bug was introduced in #2414 where only FHIR resources at the root element can be deserialized, but resources found as sub-elements of a non-resource component will fail. This was tested on both Bundle.EntryComponent and Parameters.ParameterComponent, but would apply to any element class that derives from BackBoneElement and contains a Resource.

When attempting to deserialize a Bundle.EntryComponent or a Parameters.ParameterComponent that has a valid Resource property set, the PathStack class will throw an exception. During deserialization the class will push on the _paths member when EnterElement is called and not push when EnterResource is called due to the _paths member already having an entry. Then, when ExitResource is called it will _paths.Pop() and then again when ExitElement is called which causes the exception.

To Reproduce
Deserialization of Bundle.EntryComponent

    var options = new JsonSerializerOptions().ForFhir(ModelInfo.ModelInspector);

    var bundle_EntryComponent = new Bundle.EntryComponent()
    {
        Resource = new Basic()
        {
            Code = new CodeableConcept("http://terminology.hl7.org/CodeSystem/basic-resource-type", "referral")
        }
    };

    var jsonString = JsonSerializer.Serialize(bundle_EntryComponent, options);

    var seq = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(jsonString));

    var newJsonReader = new Utf8JsonReader(seq, true, default);

    // System.InvalidOperationException: 'Stack empty.' thrown when attempting to deserialize
    var result = JsonSerializer.Deserialize<Bundle.EntryComponent>(ref newJsonReader, options);

Deserialization of Parameters.ParameterComponent

    var options = new JsonSerializerOptions().ForFhir(ModelInfo.ModelInspector);

    var bundle_EntryComponent = new Parameters.ParameterComponent()
    {
        Name = "name",
        Resource = new Basic()
        {
            Code = new CodeableConcept("http://terminology.hl7.org/CodeSystem/basic-resource-type", "referral")
        }
    };

    var jsonString = JsonSerializer.Serialize(bundle_EntryComponent, options);

    var seq = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(jsonString));

    var newJsonReader = new Utf8JsonReader(seq, true, default);

    // System.InvalidOperationException: 'Stack empty.' thrown when attempting to deserialize
    var result = JsonSerializer.Deserialize<Parameters.ParameterComponent>(ref newJsonReader, options);

Expected behavior
A valid deserialized object is returned and no exception is thrown.

Version used:

  • FHIR Version: R4
  • Version: 5.2.0 & 5.4.0
    Note: The deserialization works in 5.1.0

Additional context
Prior to popping from the _paths Stack object, it should verify that there is an entry to pop. https://github.com/FirelyTeam/firely-net-sdk/blob/main/src/Hl7.Fhir.Base/Serialization/PathStack.cs#L103

if (_paths.Any())
    _paths.Pop();
@brianpos
Copy link
Collaborator

@cames-be I assume this was from your own code calling the serializers and not via a regular API or the FhirClient right?
And those 2 snippits should be enough to have in a unit test to verify it?

@brianpos brianpos self-assigned this Nov 19, 2023
@cames-be
Copy link
Author

@brianpos

I assume this was from your own code calling the serializers and not via a regular API or the FhirClient right?

Yes, it is our code calling the serializers directly. We have run into memory pressure issues when deserializing large Bundle resources in their entirety, so instead we deserialize the individual Bundle.EntryComponent instances while processing.

And those 2 snippits should be enough to have in a unit test to verify it?

Yes, the two code examples were provided with unit tests in mind.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants