diff --git a/firely-validator-api-tests.props b/firely-validator-api-tests.props
index 532db30b..645d2df6 100644
--- a/firely-validator-api-tests.props
+++ b/firely-validator-api-tests.props
@@ -10,7 +10,7 @@
- 5.4.1-20231211.2
+ 5.4.1-20231212.3
5.1.0
diff --git a/firely-validator-api.props b/firely-validator-api.props
index d503347c..4b960aec 100644
--- a/firely-validator-api.props
+++ b/firely-validator-api.props
@@ -19,7 +19,7 @@
- 5.4.1-20231211.2
+ 5.4.1-20231212.3
diff --git a/src/Firely.Fhir.Validation/Impl/FhirPathValidator.cs b/src/Firely.Fhir.Validation/Impl/FhirPathValidator.cs
index 25742747..713f107d 100644
--- a/src/Firely.Fhir.Validation/Impl/FhirPathValidator.cs
+++ b/src/Firely.Fhir.Validation/Impl/FhirPathValidator.cs
@@ -100,14 +100,12 @@ protected override (bool, ResultReport?) RunInvariant(IScopedNode input, Validat
{
try
{
-#pragma warning disable CS0618 // Type or member is obsolete
- var node = input as ScopedNode ?? new ScopedNode(input.AsTypedElement());
-#pragma warning restore CS0618 // Type or member is obsolete
+ ScopedNode node = input.ToScopedNode();
var context = new FhirEvaluationContext(node.ResourceContext)
{
TerminologyService = new ValidateCodeServiceToTerminologyServiceAdapter(vc.ValidateCodeService)
};
- return (predicate(input, context, vc), null);
+ return (predicate(node, context, vc), null);
}
catch (Exception e)
{
@@ -153,14 +151,12 @@ private CompiledExpression getDefaultCompiledExpression(FhirPathCompiler compile
}
}
- private bool predicate(IScopedNode input, EvaluationContext context, ValidationContext vc)
+ private bool predicate(ScopedNode input, EvaluationContext context, ValidationContext vc)
{
var compiler = vc?.FhirPathCompiler ?? DefaultCompiler;
var compiledExpression = getDefaultCompiledExpression(compiler);
-#pragma warning disable CS0618 // Type or member is obsolete
- return compiledExpression.IsTrue(input.AsTypedElement(), context);
-#pragma warning restore CS0618 // Type or member is obsolete
+ return compiledExpression.IsTrue(input, context);
}
///
diff --git a/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs b/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs
index 6cf9bc99..6aed5833 100644
--- a/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs
+++ b/src/Firely.Fhir.Validation/Impl/ReferencedInstanceValidator.cs
@@ -115,11 +115,8 @@ private record ResolutionResult(ITypedElement? ReferencedResource, AggregationMo
ResolutionResult resolution = new(null, null, null);
List evidence = new();
- if (input is not ScopedNode instance)
- throw new InvalidOperationException($"Cannot validate because input is not of type {nameof(ScopedNode)}.");
-
// First, try to resolve within this instance (in contained, Bundle.entry)
- evidence.Add(resolveLocally(instance, reference, s, out resolution));
+ evidence.Add(resolveLocally(input.ToScopedNode(), reference, s, out resolution));
// Now that we have tried to fetch the reference locally, we have also determined the kind of
// reference we are dealing with, so check it for aggregation and versioning rules.
diff --git a/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs b/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs
index 1e056d67..cda1b951 100644
--- a/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs
+++ b/src/Firely.Fhir.Validation/Schema/ValueElementNode.cs
@@ -4,7 +4,6 @@
* via any medium is strictly prohibited.
*/
-using Hl7.Fhir.ElementModel;
using Hl7.Fhir.Language;
using System.Collections.Generic;
using System.Linq;
@@ -20,8 +19,6 @@ public ValueElementNode(IScopedNode wrapped)
_wrapped = wrapped;
}
- public IScopedNode? Parent => _wrapped.Parent;
-
public string Name => "value";
public string InstanceType => TypeSpecifier.ForNativeType(_wrapped.Value.GetType()).FullName;
diff --git a/src/Firely.Fhir.Validation/Support/IScopedNode.cs b/src/Firely.Fhir.Validation/Support/IScopedNode.cs
new file mode 100644
index 00000000..c53512bb
--- /dev/null
+++ b/src/Firely.Fhir.Validation/Support/IScopedNode.cs
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2023, Firely (info@fire.ly) and contributors
+ * See the file CONTRIBUTORS for details.
+ *
+ * This file is licensed under the BSD 3-Clause license
+ * available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
+ */
+
+#nullable enable
+
+using Hl7.Fhir.ElementModel;
+
+namespace Firely.Fhir.Validation
+{
+ ///
+ /// An element within a tree of typed FHIR data with also a parent element.
+ ///
+ ///
+ /// This interface represents FHIR data as a tree of elements, including type information either present in
+ /// the instance or derived from fully aware of the FHIR definitions and types
+ ///
+#pragma warning disable CS0618 // Type or member is obsolete
+ internal interface IScopedNode : IBaseElementNavigator
+#pragma warning restore CS0618 // Type or member is obsolete
+ {
+ /*
+ *
+ ///
+ /// The parent node of this node, or null if this is the root node.
+ ///
+ IScopedNode? Parent { get; } // We don't need this probably.
+ */
+ }
+}
+
+#nullable restore
\ No newline at end of file
diff --git a/src/Firely.Fhir.Validation/Support/IScopedNodeExtensions.cs b/src/Firely.Fhir.Validation/Support/IScopedNodeExtensions.cs
new file mode 100644
index 00000000..b610368e
--- /dev/null
+++ b/src/Firely.Fhir.Validation/Support/IScopedNodeExtensions.cs
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2023, Firely (info@fire.ly) and contributors
+ * See the file CONTRIBUTORS for details.
+ *
+ * This file is licensed under the BSD 3-Clause license
+ * available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
+ */
+
+#nullable enable
+
+
+using Hl7.Fhir.ElementModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Firely.Fhir.Validation
+{
+ internal static class IScopedNodeExtensions
+ {
+ ///
+ /// Converts a to a .
+ ///
+ /// An node
+ /// An implementation of
+ /// Be careful when using this method, the returned does not implement
+ /// the methods and .
+ ///
+ [Obsolete("WARNING! For internal API use only. Turning an IScopedNode into an ITypedElement will cause problems for" +
+ "Location and Definitions. Those properties are not implemented using this method and can cause problems " +
+ "elsewhere. Please don't use this method unless you know what you are doing.")]
+ public static ITypedElement AsTypedElement(this IScopedNode node) => node switch
+ {
+ TypedElementToIScopedNodeToAdapter adapter => adapter.ScopedNode,
+ ITypedElement ite => ite,
+ _ => new ScopedNodeToTypedElementAdapter(node)
+ };
+ //node is ITypedElement ite ? ite : new ScopedNodeToTypedElementAdapter(node);
+
+ public static ScopedNode ToScopedNode(this IScopedNode node) => node switch
+ {
+ TypedElementToIScopedNodeToAdapter adapter => adapter.ScopedNode,
+ _ => throw new ArgumentException("The node is not a TypedElementToIScopedNodeToAdapter")
+ };
+
+ ///
+ /// Returns the parent resource of this node, or null if this node is not part of a resource.
+ ///
+ ///
+ ///
+ ///
+ public static IEnumerable Children(this IEnumerable nodes, string? name = null) =>
+ nodes.SelectMany(n => n.Children(name));
+ }
+}
+
+#nullable restore
\ No newline at end of file
diff --git a/src/Firely.Fhir.Validation/Support/ScopedNodeExtensions.cs b/src/Firely.Fhir.Validation/Support/ScopedNodeExtensions.cs
index c4a578bb..dfc767c6 100644
--- a/src/Firely.Fhir.Validation/Support/ScopedNodeExtensions.cs
+++ b/src/Firely.Fhir.Validation/Support/ScopedNodeExtensions.cs
@@ -14,6 +14,15 @@ namespace Firely.Fhir.Validation
///
internal static class ScopedNodeExtensions
{
+
+ ///
+ /// Convert a to a .
+ ///
+ /// An
+ ///
+ public static IScopedNode AsScopedNode(this ITypedElement node)
+ => new TypedElementToIScopedNodeToAdapter(node.ToScopedNode());
+
///
///
///
@@ -25,6 +34,53 @@ public static IScopedNode ToScopedNode(this IReadOnlyDictionary
inspector ??= ModelInspector.ForAssembly(node.GetType().Assembly);
return new ScopedNodeOnDictionary(inspector, node.GetType().Name, node);
}
+
+ internal static Quantity ParseQuantity(this IScopedNode instance)
+#pragma warning disable CS0618 // Type or member is obsolete
+ => instance.ParseQuantityInternal();
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ internal static T ParsePrimitive(this IScopedNode instance) where T : PrimitiveType, new()
+#pragma warning disable CS0618 // Type or member is obsolete
+ => instance.ParsePrimitiveInternal();
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ internal static Coding ParseCoding(this IScopedNode instance)
+#pragma warning disable CS0618 // Type or member is obsolete
+ => instance.ParseCodingInternal();
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ internal static ResourceReference ParseResourceReference(this IScopedNode instance)
+#pragma warning disable CS0618 // Type or member is obsolete
+ => instance.ParseResourceReferenceInternal();
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ internal static CodeableConcept ParseCodeableConcept(this IScopedNode instance)
+#pragma warning disable CS0618 // Type or member is obsolete
+ => instance.ParseCodeableConceptInternal();
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ ///
+ /// Parses a bindeable type (code, Coding, CodeableConcept, Quantity, string, uri) into a FHIR coded datatype.
+ /// Extensions will be parsed from the 'value' of the (simple) extension.
+ ///
+ ///
+ /// An Element of a coded type (code, Coding or CodeableConcept) dependin on the instance type,
+ /// or null if no bindable instance data was found
+ /// The instance type is mapped to a codable type as follows:
+ /// 'code' => code
+ /// 'Coding' => Coding
+ /// 'CodeableConcept' => CodeableConcept
+ /// 'Quantity' => Coding
+ /// 'Extension' => depends on value[x]
+ /// 'string' => code
+ /// 'uri' => code
+ ///
+ internal static Element ParseBindable(this IScopedNode instance)
+#pragma warning disable CS0618 // Type or member is obsolete
+ => instance.ParseBindableInternal();
+#pragma warning restore CS0618 // Type or member is obsolete
+
}
internal class ScopedNodeOnDictionary : IScopedNode
diff --git a/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs b/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs
new file mode 100644
index 00000000..08dccf17
--- /dev/null
+++ b/src/Firely.Fhir.Validation/Support/ScopedNodeToTypedElementAdapter.cs
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023, Firely (info@fire.ly) and contributors
+ * See the file CONTRIBUTORS for details.
+ *
+ * This file is licensed under the BSD 3-Clause license
+ * available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
+ */
+
+#nullable enable
+
+using Hl7.Fhir.ElementModel;
+using Hl7.Fhir.Specification;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Firely.Fhir.Validation
+{
+ ///
+ /// An adapter from to .
+ ///
+ /// Be careful, this adapter does not implement the and
+ /// property.
+ ///
+ internal class ScopedNodeToTypedElementAdapter : ITypedElement
+ {
+ private readonly IScopedNode _adaptee;
+
+ public ScopedNodeToTypedElementAdapter(IScopedNode adaptee)
+ {
+ _adaptee = adaptee;
+ }
+
+ public string Location => throw new System.NotImplementedException();
+
+ public IElementDefinitionSummary Definition => throw new System.NotImplementedException();
+
+ public string Name => _adaptee.Name;
+
+ public string InstanceType => _adaptee.InstanceType;
+
+ public object Value => _adaptee.Value;
+
+ public IEnumerable Children(string? name = null) =>
+ _adaptee.Children(name).Select(n => new ScopedNodeToTypedElementAdapter(n));
+ }
+}
+
+#nullable restore
\ No newline at end of file
diff --git a/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs b/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs
new file mode 100644
index 00000000..f2502f84
--- /dev/null
+++ b/src/Firely.Fhir.Validation/Support/TypedElementToIScopedNodeToAdapter.cs
@@ -0,0 +1,31 @@
+using Hl7.Fhir.ElementModel;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Firely.Fhir.Validation
+{
+ internal class TypedElementToIScopedNodeToAdapter : IScopedNode
+ {
+ private readonly ITypedElement _adaptee;
+
+ public TypedElementToIScopedNodeToAdapter(ScopedNode adaptee) : this(adaptee as ITypedElement)
+ {
+ }
+
+ private TypedElementToIScopedNodeToAdapter(ITypedElement adaptee)
+ {
+ _adaptee = adaptee;
+ }
+
+ public ScopedNode ScopedNode => (ScopedNode)_adaptee; // we know that this is always a ScopedNode
+
+ public string Name => _adaptee.Name;
+
+ public string InstanceType => _adaptee.InstanceType;
+
+ public object Value => _adaptee.Value;
+
+ IEnumerable IBaseElementNavigator.Children(string? name) =>
+ _adaptee.Children(name).Select(n => new TypedElementToIScopedNodeToAdapter(n));
+ }
+}
diff --git a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs
index b460e3dc..f38aebdf 100644
--- a/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs
+++ b/test/Firely.Fhir.Validation.Compilation.Tests.Shared/ResourceSchemaValidationTests.cs
@@ -87,7 +87,7 @@ public void AvoidsRedoingProfileValidation()
vc.ResolveExternalReference = resolveTestData;
var validationState = new ValidationState();
- var result = schemaElement!.Validate(new ScopedNode(all.ToTypedElement()), vc, validationState);
+ var result = schemaElement!.Validate(new ScopedNode(all.ToTypedElement()).AsScopedNode(), vc, validationState);
result.Result.Should().Be(ValidationResult.Failure);
var issues = result.Evidence.OfType().ToList();
issues.Count.Should().Be(1); // Bundle.entry[2].resource[0] is validated twice against different profiles.
diff --git a/test/Firely.Fhir.Validation.Tests/Impl/ReferencedInstanceValidatorTests.cs b/test/Firely.Fhir.Validation.Tests/Impl/ReferencedInstanceValidatorTests.cs
index 1a3a1df8..63c48120 100644
--- a/test/Firely.Fhir.Validation.Tests/Impl/ReferencedInstanceValidatorTests.cs
+++ b/test/Firely.Fhir.Validation.Tests/Impl/ReferencedInstanceValidatorTests.cs
@@ -101,7 +101,7 @@ public void ValidateInstance(object instance, object testeeo, bool success, stri
static ResultReport test(object instance, IAssertion testee, ValidationContext vc)
{
- var te = new ScopedNode(instance.ToTypedElement());
+ var te = instance.ToTypedElement().AsScopedNode();
var asserter = te.Children("entry").First().Children("resource").Children("asserter").Single();
return testee.Validate(asserter, vc);
}