diff --git a/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs b/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs index 7728f8be..d22cf57d 100644 --- a/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs +++ b/src/Firely.Fhir.Validation/Impl/ResourceSchema.cs @@ -55,7 +55,10 @@ internal override ElementSchema WithMembers(IEnumerable members) /// internal static Canonical[] GetMetaProfileSchemas(PocoNode instance, ValidationSettings vc, ValidationState state) { - var profiles = instance.NavigateTo("meta.profile").Select(pn => new Canonical((string)pn.GetValue()!)).ToArray(); + // An entry can lack a value (e.g. when it only carries an extension), and a lenient parse keeps a non-string + // JSON value as is. Neither names a profile, so both are skipped. + var profiles = instance.NavigateTo("meta.profile").Select(pn => pn.GetValue()).OfType() + .Select(profile => new Canonical(profile)).ToArray(); return callback(vc.SelectValidationProfiles).Invoke(instance.GetLocation(), profiles, instance, vc); diff --git a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs index 501db265..d862b135 100644 --- a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs +++ b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs @@ -43,6 +43,23 @@ public void DoesValidateBasedOnActualType() result.Evidence.Should().ContainSingle(ass => ass is IssueAssertion && ((IssueAssertion)ass).IssueNumber == Issue.CONTENT_ELEMENT_CHOICE_INVALID_INSTANCE_TYPE.Code); } + [Fact] + public void ValidatesMetaProfileWithoutValue() + { + // A meta.profile entry may carry just an extension, and then names no profile to validate against. +#if STU3 + // STU3's data-absent-reason has the context datatype '*', which the validator does not accept yet. + var extension = "{\"url\":\"http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor\",\"valueCode\":\"UNK\"}"; +#else + var extension = "{\"url\":\"http://hl7.org/fhir/StructureDefinition/data-absent-reason\",\"valueCode\":\"unknown\"}"; +#endif + var json = "{\"resourceType\":\"Patient\",\"meta\":{\"_profile\":[{\"extension\":[" + extension + "]}]}}"; + var p = new FhirJsonDeserializer().DeserializeResource(json); + var schema = _fixture.SchemaResolver.GetSchema(Canonical.ForCoreType("Resource"))!; + var result = schema.Validate(p.ToPocoNode(), _fixture.NewValidationSettings()); + result.IsSuccessful.Should().BeTrue(); + } + private Meta setMetaForVersion(string uri) { #if STU3 diff --git a/test/Firely.Fhir.Validation.Tests/Impl/ResourceSchemaTests.cs b/test/Firely.Fhir.Validation.Tests/Impl/ResourceSchemaTests.cs index 29466c82..019b635d 100644 --- a/test/Firely.Fhir.Validation.Tests/Impl/ResourceSchemaTests.cs +++ b/test/Firely.Fhir.Validation.Tests/Impl/ResourceSchemaTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Hl7.Fhir.ElementModel; using Hl7.Fhir.Model; +using Hl7.Fhir.Serialization; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Linq; @@ -36,6 +37,18 @@ static Canonical[] callback(string location, Canonical[] orignalMetaProfiles, Po static Canonical[] declineAll(string location, Canonical[] orignalMetaProfiles, PocoNode input, ValidationSettings vc) => []; } + [DataTestMethod] + [DataRow("{\"resourceType\":\"Patient\",\"meta\":{\"profile\":[\"profile1\",null],\"_profile\":[null,{\"extension\":[{\"url\":\"http://example.org/ext\",\"valueString\":\"x\"}]}]}}")] + [DataRow("{\"resourceType\":\"Patient\",\"meta\":{\"profile\":[\"profile1\",5]}}")] + public void MetaProfileWithoutStringValueIsSkipped(string json) + { + var instance = new FhirJsonDeserializer(new DeserializerSettings().UsingMode(DeserializationMode.SyntaxOnly)).DeserializeResource(json).ToPocoNode(); + + var result = ResourceSchema.GetMetaProfileSchemas(instance, new ValidationSettings(), new ValidationState()); + + result.Should().BeEquivalentTo(new Canonical[] { "profile1" }); + } + [TestMethod] public void GetImposedProfilesTest() {