diff --git a/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml b/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml index d295b14875..991b943a26 100644 --- a/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml +++ b/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml @@ -1,6 +1,34 @@  - + + + CP0001 + T:Hl7.Fhir.ElementModel.ElementNodeExtensions + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoElementNodeExtensions + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.ElementModel.PrimitivePocoNode + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.ElementModel.Types.ICqlConvertible @@ -43,6 +71,20 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.Model.NodeType + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.Model.ScopedNodeExtensions + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.Rest.Prefer @@ -71,6 +113,13 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.Serialization.FhirJsonConverter`1 @@ -99,6 +148,13 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.Serialization.FhirXmlSerializationSettings @@ -176,6 +232,34 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.ElementModel.ElementNodeExtensions + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoElementNodeExtensions + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.ElementModel.PrimitivePocoNode + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.ElementModel.Types.ICqlConvertible @@ -218,6 +302,20 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.Model.NodeType + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0001 + T:Hl7.Fhir.Model.ScopedNodeExtensions + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.Rest.Prefer @@ -246,6 +344,13 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.Serialization.FhirJsonConverter`1 @@ -274,6 +379,13 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0001 + T:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0001 T:Hl7.Fhir.Serialization.FhirXmlSerializationSettings @@ -372,6 +484,111 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0002 + F:Hl7.FhirPath.Functions.EqualityOperators.TypedElementEqualityComparer + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNode.Child(System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.#ctor(Hl7.Fhir.ElementModel.PocoNodeOrList,System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.Deconstruct(Hl7.Fhir.ElementModel.PocoNodeOrList@,System.String@) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.ForPrimitive(Hl7.Fhir.Model.PrimitiveType) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.ForPrimitive``1(System.Object) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.FromList(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.PrimitiveType},System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.FromList``1(System.Collections.Generic.IEnumerable{System.Object}) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.get_Parent + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.set_Parent(Hl7.Fhir.ElementModel.PocoNodeOrList) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.ScopedNode.Children(System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.ScopedNode.get_Parent + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.ScopedNode.get_Type + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.ScopedNode.TryResolveBundleEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.ScopedNode.TryResolveContainedEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0002 M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToElementNode(Hl7.Fhir.Model.Base,System.String) @@ -379,6 +596,20 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0002 + M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToTypedElement(Hl7.Fhir.Model.Base,Hl7.Fhir.Introspection.ModelInspector,System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToTypedElementLegacy(Hl7.Fhir.Model.Base,Hl7.Fhir.Introspection.ModelInspector,System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0002 M:Hl7.Fhir.ElementModel.TypedElementParseExtensions.ParseBindableInternal(Hl7.Fhir.ElementModel.ITypedElement) @@ -603,6 +834,34 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.ElementNavFhirExtensions.HasValue(Hl7.Fhir.Model.IScopedNode) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.FhirPath.ElementNavFhirExtensions.HtmlChecks(Hl7.Fhir.Model.IScopedNode) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.FhirPath.ElementNavFhirExtensions.ToFhirValues(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode}) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.FhirPath.FhirEvaluationContext.get_ElementResolver + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0002 M:Hl7.Fhir.Introspection.ClassMapping.get_IsNestedType @@ -792,6 +1051,48 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.Children(System.String) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.get_Parent + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.get_Type + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.TryResolveBundleEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.TryResolveContainedEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.TryResolveLocalReference(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0002 M:Hl7.Fhir.Model.Quantity.ToQuantity @@ -1515,350 +1816,476 @@ CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJObject(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJObject(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(System.Reflection.Assembly,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJson(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.BackwardsCompatible(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJson(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Custom(Hl7.Fhir.Introspection.ModelInspector,System.Predicate{Hl7.Fhir.Utility.CodedException},Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirXmlPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonAsync(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Ostrich(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Recoverable(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonBytes(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Strict(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonBytesAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.SerializationFilter.ForElements(System.String[]) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ISourceNode,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions(Hl7.Fhir.Model.Element) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ITypedElement,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions``1(System.Collections.Generic.IEnumerable{``0}) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ISourceNode,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Utility.ReflectionHelper.FindPublicProperties(System.Type) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ITypedElement,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonText(System.String) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonTextAsync(System.String) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(System.Reflection.Assembly,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytes(System.Action{Newtonsoft.Json.JsonWriter}) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.BackwardsCompatible(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytesAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task}) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Custom(Hl7.Fhir.Introspection.ModelInspector,System.Predicate{Hl7.Fhir.Utility.CodedException},Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirXmlPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToString(System.Action{Newtonsoft.Json.JsonWriter},System.Boolean,System.Boolean) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Ostrich(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToStringAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Recoverable(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytes(System.Action{System.Xml.XmlWriter}) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Strict(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytesAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task}) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString(System.Action{System.Xml.XmlWriter},System.Boolean,System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString``1(``0,System.Action{``0,System.Xml.XmlWriter},System.Boolean,System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToStringAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpression.BeginInvoke(Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext,System.AsyncCallback,System.Object) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpression.EndInvoke(System.IAsyncResult) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpression.Invoke(Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpressionExtensions.IsBoolean(Hl7.FhirPath.CompiledExpression,System.Boolean,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpressionExtensions.IsTrue(Hl7.FhirPath.CompiledExpression,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpressionExtensions.Predicate(Hl7.FhirPath.CompiledExpression,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.CompiledExpressionExtensions.Scalar(Hl7.FhirPath.CompiledExpression,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXDocument(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.#ctor(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode,System.Collections.Generic.IDictionary{System.String,System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode}}) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXDocument(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.#ctor(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXml(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.#ctor(Hl7.Fhir.Model.IScopedNode) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXml(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.get_Environment lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlAsync(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.get_Resource lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.get_RootResource lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlBytes(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.get_Tracer lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlBytesAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContextExtensions.WithResourceOverrides``1(``0,Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ISourceNode,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.Expressions.SymbolTableExtensions.AddVar(Hl7.FhirPath.Expressions.SymbolTable,System.String,Hl7.Fhir.Model.IScopedNode) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ITypedElement,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.FhirPathCompilerCache.IsBoolean(Hl7.Fhir.Model.IScopedNode,System.String,System.Boolean,Hl7.FhirPath.EvaluationContext) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ISourceNode,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.FhirPathCompilerCache.IsTrue(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) lib/net8.0/Hl7.Fhir.Base.dll lib/net8.0/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ITypedElement,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.FhirPath.FhirPathCompilerCache.Predicate(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.FhirPathCompilerCache.Scalar(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.FhirPathCompilerCache.Select(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.Functions.EqualityOperators.IsEqualTo(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode,System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.Functions.EqualityOperators.IsEqualTo(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.Functions.EqualityOperators.IsEquivalentTo(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode,System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.Functions.EqualityOperators.IsEquivalentTo(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Boolean) + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0002 + F:Hl7.Fhir.ElementModel.Types.Any.ArgNullException + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + F:Hl7.Fhir.Rest.ContentType.VERSION_CONTENT_HEADER + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.SerializationFilter.ForElements(System.String[]) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + F:Hl7.Fhir.Serialization.FhirXmlException.ENCOUNTERED_DTP_REFERENCES_CODE + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions(Hl7.Fhir.Model.Element) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + F:Hl7.FhirPath.Functions.EqualityOperators.TypedElementEqualityComparer + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions``1(System.Collections.Generic.IEnumerable{``0}) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNode.Child(System.String) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.ReflectionHelper.FindPublicProperties(System.Type) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.#ctor(Hl7.Fhir.ElementModel.PocoNodeOrList,System.String) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonText(System.String) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.Deconstruct(Hl7.Fhir.ElementModel.PocoNodeOrList@,System.String@) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonTextAsync(System.String) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.ForPrimitive(Hl7.Fhir.Model.PrimitiveType) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytes(System.Action{Newtonsoft.Json.JsonWriter}) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.ForPrimitive``1(System.Object) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytesAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task}) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.FromList(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.PrimitiveType},System.String) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToString(System.Action{Newtonsoft.Json.JsonWriter},System.Boolean,System.Boolean) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.FromList``1(System.Collections.Generic.IEnumerable{System.Object}) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToStringAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.get_Parent + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytes(System.Action{System.Xml.XmlWriter}) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.PocoNodeOrList.set_Parent(Hl7.Fhir.ElementModel.PocoNodeOrList) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytesAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task}) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.ScopedNode.Children(System.String) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString(System.Action{System.Xml.XmlWriter},System.Boolean,System.Boolean) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.ScopedNode.get_Parent + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString``1(``0,System.Action{``0,System.Xml.XmlWriter},System.Boolean,System.Boolean) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.ScopedNode.get_Type + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToStringAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) - lib/net8.0/Hl7.Fhir.Base.dll - lib/net8.0/Hl7.Fhir.Base.dll + M:Hl7.Fhir.ElementModel.ScopedNode.TryResolveBundleEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - F:Hl7.Fhir.ElementModel.Types.Any.ArgNullException + M:Hl7.Fhir.ElementModel.ScopedNode.TryResolveContainedEntry(System.String,Hl7.Fhir.Model.IScopedNode@) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - F:Hl7.Fhir.Rest.ContentType.VERSION_CONTENT_HEADER + M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToElementNode(Hl7.Fhir.Model.Base,System.String) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - F:Hl7.Fhir.Serialization.FhirXmlException.ENCOUNTERED_DTP_REFERENCES_CODE + M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToTypedElement(Hl7.Fhir.Model.Base,Hl7.Fhir.Introspection.ModelInspector,System.String) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToElementNode(Hl7.Fhir.Model.Base,System.String) + M:Hl7.Fhir.ElementModel.TypedElementExtensions.ToTypedElementLegacy(Hl7.Fhir.Model.Base,Hl7.Fhir.Introspection.ModelInspector,System.String) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true @@ -2087,6 +2514,34 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.ElementNavFhirExtensions.HasValue(Hl7.Fhir.Model.IScopedNode) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.FhirPath.ElementNavFhirExtensions.HtmlChecks(Hl7.Fhir.Model.IScopedNode) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.FhirPath.ElementNavFhirExtensions.ToFhirValues(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode}) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.FhirPath.FhirEvaluationContext.get_ElementResolver + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0002 M:Hl7.Fhir.Introspection.ClassMapping.get_IsNestedType @@ -2276,6 +2731,48 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.Children(System.String) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.get_Parent + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.get_Type + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.TryResolveBundleEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.TryResolveContainedEntry(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.Fhir.Model.IScopedNode.TryResolveLocalReference(System.String,Hl7.Fhir.Model.IScopedNode@) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0002 M:Hl7.Fhir.Model.Quantity.ToQuantity @@ -2999,326 +3496,340 @@ CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJObject(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJObject(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(System.Reflection.Assembly,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJson(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.BackwardsCompatible(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJson(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Custom(Hl7.Fhir.Introspection.ModelInspector,System.Predicate{Hl7.Fhir.Utility.CodedException},Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirXmlPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonAsync(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Ostrich(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Recoverable(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonBytes(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Strict(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.ToJsonBytesAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Serialization.SerializationFilter.ForElements(System.String[]) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ISourceNode,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions(Hl7.Fhir.Model.Element) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ITypedElement,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions``1(System.Collections.Generic.IEnumerable{``0}) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ISourceNode,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Utility.ReflectionHelper.FindPublicProperties(System.Type) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ITypedElement,Newtonsoft.Json.JsonWriter,Hl7.Fhir.Serialization.FhirJsonSerializationSettings) + M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonText(System.String) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonTextAsync(System.String) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirJsonConverterFactory.#ctor(System.Reflection.Assembly,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytes(System.Action{Newtonsoft.Json.JsonWriter}) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.BackwardsCompatible(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytesAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task}) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Custom(Hl7.Fhir.Introspection.ModelInspector,System.Predicate{Hl7.Fhir.Utility.CodedException},Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirXmlPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToString(System.Action{Newtonsoft.Json.JsonWriter},System.Boolean,System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Ostrich(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToStringAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Recoverable(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytes(System.Action{System.Xml.XmlWriter}) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirSerializationEngineFactory.Strict(Hl7.Fhir.Introspection.ModelInspector,Hl7.Fhir.Serialization.FhirJsonPocoSerializerSettings,Hl7.Fhir.Serialization.FhirJsonPocoDeserializerSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytesAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task}) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXDocument(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString(System.Action{System.Xml.XmlWriter},System.Boolean,System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXDocument(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString``1(``0,System.Action{``0,System.Xml.XmlWriter},System.Boolean,System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXml(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToStringAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXml(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpression.BeginInvoke(Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext,System.AsyncCallback,System.Object) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlAsync(Hl7.Fhir.ElementModel.ISourceNode,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpression.EndInvoke(System.IAsyncResult) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpression.Invoke(Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlBytes(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpressionExtensions.IsBoolean(Hl7.FhirPath.CompiledExpression,System.Boolean,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.ToXmlBytesAsync(Hl7.Fhir.ElementModel.ITypedElement,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpressionExtensions.IsTrue(Hl7.FhirPath.CompiledExpression,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ISourceNode,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpressionExtensions.Predicate(Hl7.FhirPath.CompiledExpression,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteTo(Hl7.Fhir.ElementModel.ITypedElement,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.CompiledExpressionExtensions.Scalar(Hl7.FhirPath.CompiledExpression,Hl7.Fhir.Model.IScopedNode,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ISourceNode,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.#ctor(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode,System.Collections.Generic.IDictionary{System.String,System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode}}) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.FhirXmlBuilderExtensions.WriteToAsync(Hl7.Fhir.ElementModel.ITypedElement,System.Xml.XmlWriter,Hl7.Fhir.Serialization.FhirXmlSerializationSettings) + M:Hl7.FhirPath.EvaluationContext.#ctor(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Serialization.SerializationFilter.ForElements(System.String[]) + M:Hl7.FhirPath.EvaluationContext.#ctor(Hl7.Fhir.Model.IScopedNode) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions(Hl7.Fhir.Model.Element) + M:Hl7.FhirPath.EvaluationContext.get_Environment lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Specification.Snapshot.SnapshotGeneratorExtensions.RemoveAllConstrainedByDiffExtensions``1(System.Collections.Generic.IEnumerable{``0}) + M:Hl7.FhirPath.EvaluationContext.get_Resource lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.ReflectionHelper.FindPublicProperties(System.Type) + M:Hl7.FhirPath.EvaluationContext.get_RootResource lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonText(System.String) + M:Hl7.FhirPath.EvaluationContext.get_Tracer lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.JObjectFromJsonTextAsync(System.String) + M:Hl7.FhirPath.EvaluationContextExtensions.WithResourceOverrides``1(``0,Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytes(System.Action{Newtonsoft.Json.JsonWriter}) + M:Hl7.FhirPath.Expressions.SymbolTableExtensions.AddVar(Hl7.FhirPath.Expressions.SymbolTable,System.String,Hl7.Fhir.Model.IScopedNode) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToBytesAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task}) + M:Hl7.FhirPath.FhirPathCompilerCache.IsBoolean(Hl7.Fhir.Model.IScopedNode,System.String,System.Boolean,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToString(System.Action{Newtonsoft.Json.JsonWriter},System.Boolean,System.Boolean) + M:Hl7.FhirPath.FhirPathCompilerCache.IsTrue(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteJsonToStringAsync(System.Func{Newtonsoft.Json.JsonWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) + M:Hl7.FhirPath.FhirPathCompilerCache.Predicate(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytes(System.Action{System.Xml.XmlWriter}) + M:Hl7.FhirPath.FhirPathCompilerCache.Scalar(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToBytesAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task}) + M:Hl7.FhirPath.FhirPathCompilerCache.Select(Hl7.Fhir.Model.IScopedNode,System.String,Hl7.FhirPath.EvaluationContext) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString(System.Action{System.Xml.XmlWriter},System.Boolean,System.Boolean) + M:Hl7.FhirPath.Functions.EqualityOperators.IsEqualTo(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode,System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToString``1(``0,System.Action{``0,System.Xml.XmlWriter},System.Boolean,System.Boolean) + M:Hl7.FhirPath.Functions.EqualityOperators.IsEqualTo(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true CP0002 - M:Hl7.Fhir.Utility.SerializationUtil.WriteXmlToStringAsync(System.Func{System.Xml.XmlWriter,System.Threading.Tasks.Task},System.Boolean,System.Boolean) + M:Hl7.FhirPath.Functions.EqualityOperators.IsEquivalentTo(Hl7.Fhir.Model.IScopedNode,Hl7.Fhir.Model.IScopedNode,System.Boolean) + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0002 + M:Hl7.FhirPath.Functions.EqualityOperators.IsEquivalentTo(System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Collections.Generic.IEnumerable{Hl7.Fhir.Model.IScopedNode},System.Boolean) lib/netstandard2.1/Hl7.Fhir.Base.dll lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0005 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.get_Parent + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0005 M:Hl7.Fhir.ElementModel.Types.Any.TryConvertTo(System.Type,Hl7.Fhir.ElementModel.Types.Any@) @@ -3340,6 +3851,20 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0005 + P:Hl7.Fhir.ElementModel.PocoNodeOrList.Parent + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0005 + M:Hl7.Fhir.ElementModel.PocoNodeOrList.get_Parent + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0005 M:Hl7.Fhir.ElementModel.Types.Any.TryConvertTo(System.Type,Hl7.Fhir.ElementModel.Types.Any@) @@ -3361,6 +3886,27 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0005 + P:Hl7.Fhir.ElementModel.PocoNodeOrList.Parent + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0008 + T:Hl7.Fhir.ElementModel.PocoNode + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + + + CP0008 + T:Hl7.Fhir.ElementModel.ScopedNode + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0008 T:Hl7.Fhir.ElementModel.Types.Boolean @@ -3718,6 +4264,13 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0008 + T:Hl7.Fhir.Model.IScopedNode + lib/net8.0/Hl7.Fhir.Base.dll + lib/net8.0/Hl7.Fhir.Base.dll + true + CP0008 T:Hl7.Fhir.Model.Markdown @@ -3865,6 +4418,20 @@ lib/net8.0/Hl7.Fhir.Base.dll true + + CP0008 + T:Hl7.Fhir.ElementModel.PocoNode + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + + + CP0008 + T:Hl7.Fhir.ElementModel.ScopedNode + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0008 T:Hl7.Fhir.ElementModel.Types.Boolean @@ -4222,6 +4789,13 @@ lib/netstandard2.1/Hl7.Fhir.Base.dll true + + CP0008 + T:Hl7.Fhir.Model.IScopedNode + lib/netstandard2.1/Hl7.Fhir.Base.dll + lib/netstandard2.1/Hl7.Fhir.Base.dll + true + CP0008 T:Hl7.Fhir.Model.Markdown diff --git a/src/Hl7.Fhir.Base/ElementModel/ElementNodeExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/ElementNodeExtensions.cs deleted file mode 100644 index ff498859cf..0000000000 --- a/src/Hl7.Fhir.Base/ElementModel/ElementNodeExtensions.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018, 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.Adapters; -using Hl7.Fhir.Model; -using Hl7.Fhir.Specification; -using Hl7.Fhir.Utility; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace Hl7.Fhir.ElementModel -{ - public static class ElementNodeExtensions - { - public static IEnumerable Children(this IEnumerable nodes, string? name = null) => - nodes.SelectMany(n => n.Children(name)); - - public static IEnumerable Descendants(this ITypedElement element) - { - foreach (var child in element.Children()) - { - yield return child; - - foreach (var grandchild in child.Descendants()) - { - yield return grandchild; - } - } - } - - public static IEnumerable Descendants(this IEnumerable elements) => - elements.SelectMany(e => e.Descendants()); - - - public static IEnumerable DescendantsAndSelf(this ITypedElement element) => - (new[] { element }).Concat(element.Descendants()); - - public static IEnumerable DescendantsAndSelf(this IEnumerable elements) => - elements.SelectMany(e => e.DescendantsAndSelf()); - - public static void Visit(this ITypedElement root, Action visitor) => root.visit(visitor, 0); - - private static void visit(this ITypedElement root, Action visitor, int depth = 0) - { - visitor(depth, root); - - foreach (var child in root.Children()) - { - visit(child, visitor, depth + 1); - } - } - - public static IDisposable Catch(this ITypedElement source, ExceptionNotificationHandler handler) => - source is IExceptionSource s ? s.Catch(handler) : throw new NotImplementedException("Element does not implement IExceptionSource."); - - public static void VisitAll(this ITypedElement nav) => nav.Visit((_, n) => - { - var dummyValue = n.Value; - var dummyDefinition = n.Definition; - }); - - public static List VisitAndCatch(this ITypedElement node) - { - var errors = new List(); - - using (node.Catch((o, arg) => errors.Add(arg))) - { - node.VisitAll(); - } - - return errors; - } - - - - public static IEnumerable Annotations(this ITypedElement nav, Type type) => - nav is IAnnotated ann ? ann.Annotations(type) : Enumerable.Empty(); - public static T? Annotation(this ITypedElement nav) => - nav is IAnnotated ann ? ann.Annotation() : default; - - public static ISourceNode ToSourceNode(this ITypedElement node) => new TypedElementToSourceNodeAdapter(node); - - public static IReadOnlyCollection ChildDefinitions(this ITypedElement me, - IStructureDefinitionSummaryProvider provider) - { - if (me.Definition != null) - { - // If this is a backbone element, the child type is the nested complex type - if (me.Definition.Type[0] is IStructureDefinitionSummary be) - return be.GetElements(); - else - { - if (me.InstanceType != null) - { - var si = provider.Provide(me.InstanceType); - if (si != null) return si.GetElements(); - } - } - - } - - // Note: fall-through in all failure cases - return empty collection - return new List(); - } - - public static IScopedNode ToScopedNode(this ITypedElement node) => - node as IScopedNode ?? new ScopedNode(node); - - internal static IEnumerable ToScopedNodes(this IEnumerable nodes) => - nodes.Select(n => n.ToScopedNode()); - } -} - -#nullable restore \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/ISourceNode.cs b/src/Hl7.Fhir.Base/ElementModel/ISourceNode.cs index d6929f7ee6..a0baf92704 100644 --- a/src/Hl7.Fhir.Base/ElementModel/ISourceNode.cs +++ b/src/Hl7.Fhir.Base/ElementModel/ISourceNode.cs @@ -8,52 +8,51 @@ using System.Collections.Generic; -namespace Hl7.Fhir.ElementModel +namespace Hl7.Fhir.ElementModel; + +/// +/// A node within a tree of FHIR data. +/// +/// +/// This interface is typically implemented by a parser for one of the low-level serialization formats for FHIR, i.e. +/// FHIR xml/json/rdf or v3 XML. The interface does not depend on the availability of FHIR metadata and definitions +/// (in contrast to ), so the names of the nodes will have their type suffixes (for choice types) +/// and all primitives values are represented as strings, instead of native objects. +/// Implementations of this interface that want to report errors while parsing should only do so on the +/// function and getter. +/// +public interface ISourceNode { /// - /// A node within a tree of FHIR data. + /// Gets the name of the node, e.g. "active", "valueQuantity". /// - /// - /// This interface is typically implemented by a parser for one of the low-level serialization formats for FHIR, i.e. - /// FHIR xml/json/rdf or v3 XML. The interface does not depend on the availability of FHIR metadata and definitions - /// (in contrast to ), so the names of the nodes will have their type suffixes (for choice types) - /// and all primitives values are represented as strings, instead of native objects. - /// Implementations of this interface that want to report errors while parsing should only do so on the - /// function and getter. + /// Since the node has no type information, choice elements are represented as their + /// name on the wire, possibly including the type suffix for choice elements. /// - public interface ISourceNode - { - /// - /// Gets the name of the node, e.g. "active", "valueQuantity". - /// - /// Since the node has no type information, choice elements are represented as their - /// name on the wire, possibly including the type suffix for choice elements. - /// - string Name { get; } + string Name { get; } - /// - /// Gets the text of the primitive value of the node - /// - /// Returns the raw textual value as represented in the serialization, or null if there is no value in this node. - string Text { get; } + /// + /// Gets the text of the primitive value of the node + /// + /// Returns the raw textual value as represented in the serialization, or null if there is no value in this node. + string Text { get; } - /// - /// Gets the location of this node within the tree of data. - /// - /// A string of dot-separated names representing the path to the node within the tree, including indices - /// to distinguish repeated occurences of an element. - string Location { get; } + /// + /// Gets the location of this node within the tree of data. + /// + /// A string of dot-separated names representing the path to the node within the tree, including indices + /// to distinguish repeated occurences of an element. + string Location { get; } - /// - /// Enumerates the direct child nodes of the current node (if any). - /// - /// Optional. The name filter for the children. Can be omitted to not filter by name. - /// The children of the node matching the given filter, or all children if no filter was specified. - /// If no children match the given filter, the function returns an empty enumerable. - /// - /// If the parameter ends in an asterix ('*'), - /// the function will return the children of which the name starts with the given name. - /// Repeating elements will always be returned consecutively. - IEnumerable Children(string name = null); - } + /// + /// Enumerates the direct child nodes of the current node (if any). + /// + /// Optional. The name filter for the children. Can be omitted to not filter by name. + /// The children of the node matching the given filter, or all children if no filter was specified. + /// If no children match the given filter, the function returns an empty enumerable. + /// + /// If the parameter ends in an asterix ('*'), + /// the function will return the children of which the name starts with the given name. + /// Repeating elements will always be returned consecutively. + IEnumerable Children(string name = null); } \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs b/src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs index b6d83b4b82..d7c40f1fd1 100644 --- a/src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs +++ b/src/Hl7.Fhir.Base/ElementModel/NewPocoBuilder.cs @@ -141,7 +141,7 @@ private ClassMapping classMappingForElement(ITypedElement node, PropertyMapping? // Otherwise, let's use the ITypedElement's instance type. if (node.InstanceType is { } instanceType && - inspector.FindClassMapping(instanceType) is { NativeType.IsAbstract: false } mapping) + inspector.FindClassMapping(instanceType) is { NativeType.IsAbstract: false } mapping && typeof(Base).IsAssignableFrom(mapping.NativeType)) return mapping; // No useable concrete type in the property, nor in the instance type, so we need to create @@ -183,7 +183,7 @@ private ClassMapping determineBestDynamicMappingForElement(ITypedElement node) if (node.Value is not null || (node.InstanceType is { } it && char.IsLower(it[0]))) return determineBestPrimitiveMapping(); - if (node.Annotation() is not null) + if (node.Annotation() is not null || node.Definition?.IsResource is true) return getClassMapping(DYNAMIC_RESOURCE_TYPE_NAME); return getClassMapping(DYNAMIC_DATATYPE_TYPE_NAME); diff --git a/src/Hl7.Fhir.Base/ElementModel/PocoBuilderExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/PocoBuilderExtensions.cs deleted file mode 100644 index d50f3700f1..0000000000 --- a/src/Hl7.Fhir.Base/ElementModel/PocoBuilderExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018, 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://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE - */ - - -using Hl7.Fhir.Introspection; -using Hl7.Fhir.Model; -using Hl7.Fhir.Serialization; -using Hl7.Fhir.Utility; -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Hl7.Fhir.ElementModel -{ - public static class PocoBuilderExtensions - { - public static Base ToPoco(this ISourceNode source, ModelInspector inspector, Type pocoType = null, PocoBuilderSettings settings = null) => - new LegacyPocoBuilder(inspector, settings).BuildFrom(source, pocoType); - - public static T ToPoco(this ISourceNode source, ModelInspector inspector, PocoBuilderSettings settings = null) where T : Base => - (T)source.ToPoco(inspector, typeof(T), settings); - - public static T ToPoco(this ITypedElement element, ModelInspector inspector, PocoBuilderSettings settings = null) where T : Base => - (T)new NewPocoBuilder(inspector, settings ?? new PocoBuilderSettings()).BuildFrom(element); - - public static Base ToPoco(this ITypedElement element, ModelInspector inspector, PocoBuilderSettings settings = null) => - new NewPocoBuilder(inspector, settings ?? new PocoBuilderSettings()).BuildFrom(element); - - public static ISourceNode ToSourceNode(this Base @base, ModelInspector inspector, string rootName = null) => - @base.ToPocoNode(inspector, rootName); - } -} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/PocoNode.Primitives.cs b/src/Hl7.Fhir.Base/ElementModel/PocoNode.Primitives.cs new file mode 100644 index 0000000000..c1091dc9d8 --- /dev/null +++ b/src/Hl7.Fhir.Base/ElementModel/PocoNode.Primitives.cs @@ -0,0 +1,96 @@ +using Hl7.Fhir.Model; +using Hl7.FhirPath; +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace Hl7.Fhir.ElementModel; + +public partial record PocoNode +{ + /// + /// Constructs a PocoNode from a PrimitiveType + /// + /// + public static PocoNode ForPrimitive(PrimitiveType primitive) => + new PrimitiveNode(primitive, null, null); + + + /// + /// Constructs a PocoNode from an object. Allowed objects are those that can be converted to a PrimitiveType, and are not yet PrimitiveTypes. + /// + /// + public static PocoNode ForAnyPrimitive(object value) + { + return ForPrimitive(PrimitiveNode.InferFromValue(value)); + } + + /// + /// Constructs a PocoNode from a value and a type. The type must be a PrimitiveType. + /// + /// + /// + /// + public static PocoNode ForPrimitive(object value) where T : PrimitiveType, new() => + new PrimitiveNode(new T { ObjectValue = value }, null, null); + + /// + /// Constructs a PocoNode from a list of PrimitiveTypes + /// + /// + /// + /// + public static IEnumerable FromList(IEnumerable primitives, string? name = null) => + primitives.Select(ForPrimitive); + + /// + /// Constructs multiple PocoNodes from a list of values and a type. The type must be a PrimitiveType. + /// + /// + /// + /// + public static IEnumerable FromList(IEnumerable values) where T : PrimitiveType, new() => + values.Select(ForPrimitive); + + /// + /// Constructs multiple PocoNodes from a list of objects. Allowed objects are those that can be converted to a PrimitiveType, and are not yet PrimitiveTypes. + /// + /// + /// + public static IEnumerable FromAnyList(IEnumerable values) => + values.Select(v => v as PocoNode ?? ForAnyPrimitive(v)); +} + +public record PrimitiveNode(PrimitiveType Primitive, PocoNodeOrList? ParentNode, int? Index, string? Name = null) : PocoNode(Primitive, ParentNode, Index, Name) +{ + protected override object? ValueInternal => Primitive.ToITypedElementValue(); + internal object? Value => ValueInternal; + + internal static PrimitiveType InferFromValue(object value) => value switch + { + Types.Quantity qt => new FPQuantity(qt), + Types.DateTime dt => new FPDateTime(dt), + Types.Date d => new FPDate(d), + Types.Time t => new FPTime(t), + decimal dec => new FPDecimal(dec), + float f => new FPDecimal((decimal)f), + double d => new FPDecimal((decimal)d), + bool b => new FPBoolean(b), + int i => new FPInteger(i), + long l => new FPLong(l), + string s => new FPString(s), + _ => throw new ArgumentException("Cannot infer primitive type from value", nameof(value)) + }; + + protected override string? TextInternal => Primitive.ToString(); +} + +internal record PrimitiveListNode(IReadOnlyList Primitives, PocoNodeOrList? ParentNode, string? Name = null) : PocoListNode(Primitives, ParentNode, Name ?? "value") +{ + public override IEnumerator GetEnumerator() => + Primitives.Select((primitive, index) => new PrimitiveNode(primitive, ParentNode, index, Name)).GetEnumerator(); + + internal IEnumerable Values => Primitives.Select(p => p.ObjectValue); +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/PocoNode.TypedElement.cs b/src/Hl7.Fhir.Base/ElementModel/PocoNode.TypedElement.cs new file mode 100644 index 0000000000..47b9834bcc --- /dev/null +++ b/src/Hl7.Fhir.Base/ElementModel/PocoNode.TypedElement.cs @@ -0,0 +1,91 @@ +using Hl7.Fhir.Introspection; +using Hl7.Fhir.Model; +using Hl7.Fhir.Specification; +using Hl7.Fhir.Utility; +using Hl7.FhirPath; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace Hl7.Fhir.ElementModel; + +public partial record PocoNode +{ + string ITypedElement.InstanceType => + Poco switch + { + DataType => Poco.TypeName, + BackboneElement => "BackboneElement", + Element when Poco.TypeName.Contains('.') => "Element", + _ => Poco.TypeName + }; + + object? ITypedElement.Value => ValueInternal; + + // needed for ITE + protected virtual object? ValueInternal => null; + + string ITypedElement.Location => (Index, Parent) switch + { + // if we have an index, write it + ({ } idx, { } parent) => $"{((ITypedElement)parent).Location}.{Name}[{idx}]", + // if we do not, write 0 as idx + (_, { } parent) => $"{((ITypedElement)parent).Location}.{Name}[0]", + // if we have neither, we are the root. + _ => Name + }; + + IElementDefinitionSummary? ITypedElement.Definition + { + get + { + if (this.FindInspector() is not { } inspector) + return null; + + if (this.Parent is not {} node) + return ElementDefinitionSummary.ForRoot(inspector.FindOrImportClassMapping(Poco.GetType()), Name); + + var classMapping = inspector.FindOrImportClassMapping(node.Poco.GetType()); + return classMapping?.FindMappedElementByName(Name); + } + } + + IEnumerable ITypedElement.Children(string? name) => name is null + ? Children().SelectMany(node => node) + : Child(name) ?? Enumerable.Empty(); + + protected virtual string? TextInternal => null; + string? ISourceNode.Text => TextInternal; + + private Lazy SourceName => new (() => + Poco is DataType { TypeName: var tn } && + ((ITypedElement)this).Definition!.IsChoiceElement + ? Name + tn.Capitalize() + : Name + ); + + string ISourceNode.Location => + (Index, Parent) switch + { + // if we have an index, write it + ({ } idx, { } parent) => $"{((ITypedElement)parent).Location}.{SourceName.Value}[{idx}]", + // if we do not, write 0 as idx + (_, { } parent) => $"{((ITypedElement)parent).Location}.{SourceName.Value}[0]", + // if we have neither, we are the root. + _ => SourceName.Value + }; + + IEnumerable ISourceNode.Children(string? name) + { + if (name is null) return Children().SelectMany(node => node); + + var trueElementName = this.FindInspector()? + .FindOrImportClassMapping(Poco.GetType())? + .FindMappedElementByChoiceName(name)?.Name; + + return Child(trueElementName ?? name) ?? Enumerable.Empty(); + } +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/PocoNodeExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/PocoNodeExtensions.cs new file mode 100644 index 0000000000..9ebbd27eef --- /dev/null +++ b/src/Hl7.Fhir.Base/ElementModel/PocoNodeExtensions.cs @@ -0,0 +1,304 @@ +using Hl7.Fhir.Introspection; +using Hl7.Fhir.Model; +using Hl7.Fhir.Rest; +using Hl7.Fhir.Serialization; +using Hl7.Fhir.Utility; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Hl7.Fhir.ElementModel; + +#nullable enable + +public static class PocoNodeExtensions +{ + private static bool tryResolveBundleEntry(this PocoNode? node, string fullUrl, [NotNullWhen(true)] out PocoNode? result) + { + result = node?.Poco is Bundle + ? node + .Child("entry") + ?.FirstOrDefault(entry => + entry.FullUrl == fullUrl) + ?.Child("resource") + : null; + return result is not null; + } + + private static bool tryResolveContainedEntry(this PocoNode? node, string? id, [NotNullWhen(true)] out PocoNode? result) + { + result = node?.Poco is DomainResource + ? node + .Child("contained") + ?.FirstOrDefault(contained => $"#{contained.Id}" == id) + : null; + return result is not null; + } + + /// + /// Resolve a resource reference within the context of this node given a url (for bundles) or id (for contained). + /// + /// this node + /// The relative URL to resolve. + /// Contains the referenced instance, or null if the operation failed + /// Does not create a copy. The resolved resource will be part of the PocoNode-tree that was passed to this function + /// t + internal static bool TryResolveLocalReference(this PocoNode? node, string url, [NotNullWhen(true)] out PocoNode? result) + { + for(var scan = node; scan is not null; scan = scan.Parent) + { + if (scan.Poco is Bundle) // if we do not find it in the closest bundle, the reference is invalid + { + return scan.tryResolveBundleEntry(url, out result); + } + + if (scan.Poco is DomainResource && scan.tryResolveContainedEntry(url, out result)) + { + // if we encounter a DomainResource, try to resolve the contained reference. + // If it fails, higher domain resources could still contain it! + return true; + } + + if (scan.Child("id")?.Value as string == url[1..]) + { + // if we encounter a resource with the correct id, return it + result = scan; + return true; + } + } + + result = null; + return false; + } + + private static IEnumerable parents(this PocoNodeOrList node) + { + for(var scan = node.Parent; scan is not null; scan = scan.Parent) + { + yield return scan; + } + } + + private static PocoNode? getContainer(this PocoNodeOrList node) + { + var scan = node; + while(scan is not (null or { Name: "contained" })) + { + scan = scan.Parent; // navigate up to "contained" + } + + return scan?.Parent; // return the container (DomainResource around contained) + } + + /// + /// Resolve the given reference within the context of the given node. This node should be of type reference. + /// + /// A node representing a reference + /// An external resolver + /// + /// Does not create a copy. The resolved resource will be part of the PocoNode-tree that was passed to this function + public static PocoNode? Resolve(this PocoNode? node, Func? externalResolver = null) + { + if (node is null) return null; + + string? url = node.Poco switch + { + Canonical c => c.Value, // canonicals can be references + ResourceReference r => r.Reference, + PrimitiveType {ObjectValue: string s} => s, + _ => throw new ArgumentException($"Error occurred during reference resolution: Parameter {nameof(node)} is not a reference.") + }; + + return url is null ? null : Resolve(node, url, externalResolver); + } + + /// + /// Resolve the given url within the context of the given node + /// + /// The context for the reference resolution + /// The reference to be resolved + /// + /// Does not create a copy. The resolved resource will be part of the PocoNode-tree that was passed to this function + /// + public static PocoNode? Resolve(this PocoNode? node, string url, Func? externalResolver = null) + { + if (node is null) return null; + + if(url == "#") return node.getContainer(); + + var identity = node.MakeAbsolute(new ResourceIdentity(url)); + if (node.TryResolveLocalReference(identity.ToString(), out var localResult)) return localResult; + + return externalResolver?.Invoke(url); + } + + /// + /// Extract the %resource variable from this PocoNode + /// + internal static PocoNode? GetResourceContext(this PocoNodeOrList? node) => node switch + { + PocoListNode { Parent: null } => null, // if parent is null, do not go further. If we are repeating and we don't have a parent, something went seriously wrong + PocoNode { Parent: null } single => single, + PocoNode { Poco: Resource } single => single, // if resource, return itself + _ => node?.Parent?.GetResourceContext() // otherwise, go to parent + }; + + /// + /// Extract the %rootResource variable from this PocoNode + /// + internal static PocoNode? GetRootResourceContext(this PocoNodeOrList node) => node.GetResourceContext() switch + { + { Name : "contained" } containedResource => containedResource.Parent!, // if contained, return container + { } resource => resource, // otherwise return %resource + _ => null + }; + + /// + /// Find the fullUrl of the bundle entry that contains this node + /// + /// + /// + internal static string? FindFullUrl(this PocoNodeOrList node) + { + var entry = node.parents().FirstOrDefault(n => n.Poco is Bundle.EntryComponent); + return entry?.Child("fullUrl")?.Value as string; + } + + /// + /// Turn a relative reference into an absolute url, based on the fullUrl of the parent resource + /// + /// See https://www.hl7.org/fhir/bundle.html#references for more information + internal static ResourceIdentity MakeAbsolute(this PocoNode node, ResourceIdentity identity) + { + if (!identity.IsRelativeRestUrl) return identity; + // Relocate the relative url on the base given in the fullUrl of the entry (if applicable) + var fullUrl = node.FindFullUrl(); + + if (fullUrl == null) return identity; + + var parentIdentity = new ResourceIdentity(fullUrl); + + if (parentIdentity.IsAbsoluteRestUrl) + identity = identity.WithBase(parentIdentity.BaseUri); + else if (parentIdentity.IsUrn) + identity = new ResourceIdentity($"{parentIdentity}/{identity.Id}"); + + // Return the identity - will remain relative if we did not find a fullUrl + + return identity; + } + + /// + /// Turn a relative reference into an absolute url, based on the fullUrl of the parent resource + /// + /// See https://www.hl7.org/fhir/bundle.html#references for more information + public static string MakeAbsolute(this PocoNode node, string reference) => + node.MakeAbsolute(new ResourceIdentity(reference)).ToString(); + + internal static PocoNode? GetParentResource(this PocoNodeOrList node) => node.parents().FirstOrDefault(parentNode => parentNode is { Poco: Resource }); + + internal static string GetLocation(this PocoNode node) => ((ITypedElement)node).Location; + + internal static string GetLocalLocation(this PocoNode node) => + node.Parent is null + ? node.GetLocation() + : $"{((IResourceTypeSupplier)node.GetParentResource()!).ResourceType}.{node.GetLocation()[(node.GetParentResource()!.GetLocation().Length + 1)..]}"; + + /// + /// Returns the contained resources of a DomainResource node + /// + /// + /// + public static IEnumerable ContainedResources(this PocoNode node) => node.Child("contained") ?? Enumerable.Empty(); + + /// + /// Returns the bundle entries of a Bundle node + /// + /// + /// + public static IEnumerable BundledResources(this PocoNode node) => node.Child("entry") ?? Enumerable.Empty(); + + /// + /// Gets the "Legacy" ITypedElement.Value of a PocoNode without having to do an explicit cast. Uses a function signature to indicate its potential cost. + /// + /// + /// + public static object? GetValue(this PocoNode node) => ((ITypedElement)node).Value; + + /// + /// Searches all given nodes for the specified children and flattens the results. + /// + /// + /// + /// + public static IEnumerable FlatChildren(this IEnumerable nodes, string name) => nodes.SelectMany(node => node.Child(name) ?? Enumerable.Empty()); + + /// + /// Finds all descendants of this node and flattens the result. + /// + /// + /// + public static IEnumerable Descendants(this IEnumerable nodes) => nodes.SelectMany(descendants); + + /// + /// Finds this node and all descendants of this node and flattens the result. + /// + /// + /// + public static IEnumerable DescendantsAndSelf(this IEnumerable nodes) => nodes.Descendants().Concat(nodes); + + private static IEnumerable descendants(this PocoNode node) => node.Children().SelectMany(singleOrList => singleOrList).DescendantsAndSelf(); + + internal static T? Child(this PocoNode? node, string name) where T : PocoNodeOrList => node?.Child(name) as T; + + /// + /// Filters a list of nodes based on a predicate to be evaluated on their internal POCOs. + /// + /// + /// + /// + /// + public static IEnumerable Where(this IEnumerable nodes, Func predicate) where T : Base => + nodes is PocoListNode pln + ? pln.Where(predicate) + : nodes.Where(n => n.Poco is T t && predicate(t)); + + internal static IEnumerable Where(this PocoListNode pln, Func predicate) where T : Base => + pln.Pocos.OfType().Where(predicate).Select((poco, index) => new PocoNode(poco, pln.Parent, index, pln.Name)); + + /// + /// Finds the first node in a list of nodes that satisfies a predicate to be evaluated on their internal POCOs. + /// + /// + /// + /// + /// + public static PocoNode? FirstOrDefault(this IEnumerable node, Func predicate) where T : Base => + node.FirstOrDefault(n => n.Poco is T t && predicate(t)); + + internal static PocoNode? FirstOrDefault(this PocoListNode pln, Func predicate) where T : Base + { + for (int index = 0; index < pln.Pocos.Count; index++) + { + if (pln.Pocos[index] is T item && predicate(item)) + return new PocoNode(item, pln.Parent, index, pln.Name); + } + + return null; + } + + internal static ModelInspector? FindInspector(this PocoNode node) => ((IAnnotated)node).Annotation() ?? node.Parent?.SingleOrDefault()?.FindInspector(); + + internal static string SerializeToString(this PocoNode pn, bool pretty) + { + var serializer = new BaseFhirXmlSerializer(pn.FindInspector() ?? ModelInspector.ForType(pn.Poco.GetType())); + + // If we are serializing a subtree of a resource, then if the current node is a datatype or a nested resource, + // we need to pick a name for this root element. + var pickElementName = pn.Poco is not Resource || pn.Parent is not null; + var rootName = pickElementName ? pn.Name : null; + + return serializer.SerializeToString(pn.Poco, pretty, rootName: rootName); + } +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/PocoNodeOrList.cs b/src/Hl7.Fhir.Base/ElementModel/PocoNodeOrList.cs index 42bc812cdb..175726cf03 100644 --- a/src/Hl7.Fhir.Base/ElementModel/PocoNodeOrList.cs +++ b/src/Hl7.Fhir.Base/ElementModel/PocoNodeOrList.cs @@ -1,63 +1,80 @@ -using Hl7.Fhir.Introspection; using Hl7.Fhir.Model; -using Hl7.Fhir.Specification; using Hl7.Fhir.Utility; using System; using System.Collections; using System.Collections.Generic; -using System.Data.Common; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; namespace Hl7.Fhir.ElementModel; #nullable enable -public abstract record PocoNodeOrList(PocoNodeOrList? Parent, string Name) : IEnumerable +/// +/// A singular node in a POCO node tree. This node represents either a repeating or singular POCO instance. +/// +/// +public abstract record PocoNodeOrList(string Name) : IEnumerable { + /// + /// The parent of this node. This is always a singular PocoNode. If the Parent field is set to a PocoListNode, this will construct and return the PocoNode at the specified index. + /// + public abstract PocoNode? Parent { get; } + public abstract IEnumerator GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public static PocoNode Root(Base @base, string? name = null) => @base switch { - PrimitiveType primitive => new PrimitivePocoNode(primitive, name), + PrimitiveType primitive => new PrimitiveNode(primitive, null, null, name), { } b => new PocoNode(b, null, null, name) }; - - public static PocoNode ForPrimitive(PrimitiveType primitive) => - new PrimitivePocoNode(primitive); - - public static PocoNode ForPrimitive(object value) where T : PrimitiveType, new() => - new PrimitivePocoNode(new T { ObjectValue = value }); - - public static IEnumerable FromList(IEnumerable primitives, string? name = null) => - primitives.Select(ForPrimitive); - - public static IEnumerable FromList(IEnumerable values) where T : PrimitiveType, new() => - values.Select(ForPrimitive); } -public record PocoNode(Base Poco, PocoNodeOrList? Parent, int? Index, string? Name) - : PocoNodeOrList(Parent, Name ?? Poco.TypeName), IScopedNode, ISourceNode, IFhirValueProvider, IResourceTypeSupplier, IAnnotatable +/// +/// A singular node in a POCO node tree. This node represents a single POCO instance. +/// +/// +/// +/// This Poco's index in a list, if it is contained in one +/// +public partial record PocoNode(Base Poco, PocoNodeOrList? ParentNode, int? Index, string? Name) + : PocoNodeOrList(Name ?? Poco.TypeName), ITypedElement, IShortPathGenerator, ISourceNode, IFhirValueProvider, IResourceTypeSupplier, IAnnotatable { + /// + public override PocoNode? Parent => ParentNode switch + { + PocoListNode nodes => nodes[Index!.Value], + PocoNode node => node, + _ => null + }; + + /// + /// Enumerates all children of this node. These can each either be singular or repeating PocoNodes. + /// + /// + /// Since PocoNodeOrList implements IEnumerable of PocoNode, you can consider this to be an IEnumerable of IEnumerable of PocoNode, if you prefer to work with that public IEnumerable Children() => Poco.EnumerateElements() .Select(ep => nodeFor(ep.Key, ep.Value) ); - public IEnumerable? Child(string name) => Poco.TryGetValue(name, out var result) + /// + /// Finds a single child of this node by name. The result is either a singular or repeating PocoNode. The return value can always be used as an IEnumerable of PocoNode. + /// + /// + /// + public PocoNodeOrList? Child(string name) => Poco.TryGetValue(name, out var result) ? nodeFor(name, result) : null; private PocoNodeOrList nodeFor(string name, object value) => value switch { - PrimitiveType primitive => new PrimitivePocoNode(primitive, name) { Parent = this }, + PrimitiveType primitive => new PrimitiveNode(primitive, null, null, name) { ParentNode = this }, Base b => new PocoNode(b, this, null, name), - IEnumerable primitiveList => new PrimitivePocoListNode(primitiveList.ToList(), name) { Parent = this }, + IEnumerable primitiveList => new PrimitiveListNode(primitiveList.ToList(), null, name) { ParentNode = this }, IEnumerable list => new PocoListNode(list.ToList(), this, name), _ => throw new InvalidOperationException("Unexpected element in child list") }; @@ -65,7 +82,10 @@ private PocoNodeOrList nodeFor(string name, object value) => private IEnumerable asList() => [this]; public override IEnumerator GetEnumerator() => asList().GetEnumerator(); - + + #region << Annotations >> + + /// string IShortPathGenerator.ShortPath => (Index, Parent) switch { // if we have an index, we have a parent. @@ -76,153 +96,22 @@ private PocoNodeOrList nodeFor(string name, object value) => _ => Name }; + /// Base IFhirValueProvider.FhirValue => Poco; + /// string? IResourceTypeSupplier.ResourceType => Poco is Resource ? ((ITypedElement)this).InstanceType : null; - - #region ITypedElement - - string ITypedElement.InstanceType => - Poco switch - { - BackboneElement => "BackboneElement", - Element when Poco.TypeName.Contains('.') => "Element", - _ => Poco.TypeName - }; - - object? ITypedElement.Value => ValueInternal; - - // needed for ITE - protected virtual object? ValueInternal => null; - - string ITypedElement.Location => (Index, Parent) switch - { - // if we have an index, write it - ({ } idx, { } parent) => $"{((ITypedElement)parent).Location}.{Name}[{idx}]", - // if we do not, write 0 as idx - (_, { } parent) => $"{((ITypedElement)parent).Location}.{Name}[0]", - // if we have neither, we are the root. - _ => Name - }; - - IElementDefinitionSummary? ITypedElement.Definition - { - get - { - if (FindInspector() is not { } inspector) - return null; - - if ((this as IScopedNode).Parent is not PocoNode node) - return ElementDefinitionSummary.ForRoot(inspector.FindOrImportClassMapping(Poco.GetType()), Name); - - var classMapping = inspector.FindOrImportClassMapping(node.Poco.GetType()); - return classMapping?.FindMappedElementByName(Name); - } - } - - [TemporarilyChanged] // I am refactoring the extensions in another branch. This should go into those extensions. To avoid conflicts, I implement it here for now. - internal ModelInspector? FindInspector() => ((IAnnotated)this).Annotation() ?? Parent?.SingleOrDefault()?.FindInspector(); - - IEnumerable ITypedElement.Children(string? name) => (this as IScopedNode).Children(name); - - #endregion - - #region IScopedNode - - IScopedNode? IScopedNode.Parent => Parent switch - { - PocoListNode rpen => rpen[Index!.Value], - PocoNode spen => spen, - _ => null - }; - - IEnumerable IScopedNode.Children(string? name) => name is null - ? Children().SelectMany(node => node) - : Child(name) ?? []; - - [Obsolete("This is a temporary feature and will be removed before we publish SDK 6.0.")] - NodeType IScopedNode.Type => Poco switch - { - Bundle => NodeType.Bundle | NodeType.Resource, - PrimitiveType => NodeType.Primitive, - DomainResource => NodeType.DomainResource | NodeType.Resource, - Resource => NodeType.Resource, - ResourceReference or Canonical or CodeableReference => NodeType.Reference, - Quantity => NodeType.Quantity, - _ => 0 - }; - - bool IScopedNode.TryResolveBundleEntry(string fullUrl, [NotNullWhen(true)] out IScopedNode? result) - { - result = Poco is Bundle - ? this - .Child("entry") - ?.FirstOrDefault(entry => - entry.FullUrl == fullUrl) - ?.Child("resource") - : null; - return result is not null; - } - - bool IScopedNode.TryResolveContainedEntry(string id, [NotNullWhen(true)] out IScopedNode? result) - { - result = Poco is DomainResource - ? this - .Child("contained") - ?.FirstOrDefault(contained => $"#{contained.Id}" == id) - : null; - return result is not null; - } - - #endregion - - #region ISourceNode - - protected virtual string? TextInternal => null; - string? ISourceNode.Text => TextInternal; - - private Lazy SourceName => new (() => - Poco is DataType { TypeName: var tn } && - ((ITypedElement)this).Definition!.IsChoiceElement - ? Name + tn.Capitalize() - : Name - ); - - string ISourceNode.Location => - (Index, Parent) switch - { - // if we have an index, write it - ({ } idx, { } parent) => $"{((ITypedElement)parent).Location}.{SourceName.Value}[{idx}]", - // if we do not, write 0 as idx - (_, { } parent) => $"{((ITypedElement)parent).Location}.{SourceName.Value}[0]", - // if we have neither, we are the root. - _ => SourceName.Value - }; - - IEnumerable ISourceNode.Children(string? name) - { - if (name is null) return Children().SelectMany(node => node); - - var trueElementName = FindInspector()? - .FindOrImportClassMapping(Poco.GetType())? - .FindMappedElementByChoiceName(name)?.Name; - - return Child(trueElementName ?? name) ?? []; - } - - #endregion - - #region << Annotations >> private AnnotationList? _annotations; private AnnotationList Annotations => LazyInitializer.EnsureInitialized(ref _annotations, () => [])!; + /// IEnumerable IAnnotated.Annotations(Type type) { - if (type == typeof(ITypedElement) || type == typeof(IShortPathGenerator) || type == typeof(IScopedNode)) + if (type == typeof(ITypedElement) || type == typeof(IShortPathGenerator)) return [this]; if (type == typeof(IFhirValueProvider)) return [this]; @@ -231,47 +120,24 @@ IEnumerable IAnnotated.Annotations(Type type) return Annotations.OfType(type); } + /// void IAnnotatable.AddAnnotation(object annotation) => Annotations.AddAnnotation(annotation); + /// void IAnnotatable.RemoveAnnotations(Type type) => Annotations.RemoveAnnotations(type); #endregion } -internal record PocoListNode(IReadOnlyList Pocos, PocoNodeOrList? Parent, string Name) : PocoNodeOrList(Parent, Name) +/// +/// A single node for a repeating element. Note that since a repeating element has a single parent, this cannot be used for grouping "separate" pocos that are not repeating in the specification. +/// +/// +/// +/// +internal record PocoListNode(IReadOnlyList Pocos, PocoNodeOrList? ParentNode, string Name) : PocoNodeOrList(Name) { public PocoNode this[int index] => new(Pocos[index], Parent, index, Name); - - public IEnumerable Where(Func predicate) where T : Base => - Pocos.OfType().Where(predicate).Select((poco, index) => new PocoNode(poco, Parent, index, Name)); - - public PocoNode? FirstOrDefault(Func predicate) where T : Base - { - for (int index = 0; index < Pocos.Count; index++) - { - if (Pocos[index] is T item && predicate(item)) - return new PocoNode(item, Parent, index, Name); - } - - return null; - } - + public override PocoNode? Parent => ParentNode as PocoNode; public override IEnumerator GetEnumerator() => Pocos.Select((poco, index) => new PocoNode(poco, Parent, index, Name)).GetEnumerator(); -} - -public record PrimitivePocoNode(PrimitiveType Primitive, string? Name = null) : PocoNode(Primitive, null, null, Name) -{ - protected override object? ValueInternal => Primitive.ToITypedElementValue(); - protected override string? TextInternal => Primitive.ToString(); -} - -internal record PrimitivePocoListNode(IReadOnlyList Primitives, string? Name = null) : PocoListNode(Primitives, null, Name ?? "value") -{ - public override IEnumerator GetEnumerator() => - Primitives.Select((primitive, index) => new PrimitivePocoNode(primitive, Name) { Index = index }).GetEnumerator(); -} - -public static class PocoElementNodeExtensions -{ - public static T? Child(this PocoNode? node, string name) where T : PocoNodeOrList => node?.Child(name) as T; } \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs b/src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs index ae5c1561a7..45f643e201 100644 --- a/src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs +++ b/src/Hl7.Fhir.Base/ElementModel/ScopedNode.cs @@ -18,7 +18,7 @@ namespace Hl7.Fhir.ElementModel { - public class ScopedNode : IScopedNode, IAnnotated, IExceptionSource + public class ScopedNode : ITypedElement, IShortPathGenerator, IAnnotated, IExceptionSource { private class Cache { @@ -72,7 +72,7 @@ private ScopedNode(ScopedNode parentNode, ScopedNode? parentResource, ITypedElem /// /// The resource or element which is the direct parent of this node. /// - public IScopedNode? Parent { get; } + public ScopedNode? Parent { get; } /// /// Returns the location of the current element within its most direct parent resource or datatype. @@ -87,19 +87,6 @@ private ScopedNode(ScopedNode parentNode, ScopedNode? parentResource, ITypedElem /// public string Name => Current.Name; - /// - /// Will be replaced by a different implementation in the future. - /// - public NodeType Type => this switch - { - { AtResource: true } when Current.Children("contained").Any() => NodeType.DomainResource | NodeType.Resource, - { InstanceType: FhirTypeNames.BUNDLE } => NodeType.Bundle | NodeType.Resource, - { AtResource: true } => NodeType.Resource, - { InstanceType: FhirTypeNames.REFERENCE or FhirTypeNames.CANONICAL or FhirTypeNames.CODEABLEREFERENCE } => NodeType.Reference, - { Value: not null } => NodeType.Primitive, - _ => 0 - }; - /// public string? InstanceType => Current.InstanceType; @@ -109,10 +96,10 @@ private ScopedNode(ScopedNode parentNode, ScopedNode? parentResource, ITypedElem /// public string Location => Current.Location; - public bool TryResolveBundleEntry(string fullUrl, [NotNullWhen(true)] out IScopedNode? result) + public bool TryResolveBundleEntry(string fullUrl, [NotNullWhen(true)] out ScopedNode? result) => (result = ((ReferencedResourceCache)this.BundledResources()).ResolveReference(fullUrl)) is not null; - public bool TryResolveContainedEntry(string id, [NotNullWhen(true)] out IScopedNode? result) + public bool TryResolveContainedEntry(string id, [NotNullWhen(true)] out ScopedNode? result) => (result = (this.ContainedResourcesWithId()).ResolveReference(id)) is not null; /// @@ -287,7 +274,7 @@ IEnumerable ITypedElement.Children(string? name) => Current.Children(name).Select(c => new ScopedNode(this, ParentResource, c, _fullUrl)); /// - public IEnumerable Children(string? name = null) => + public IEnumerable Children(string? name = null) => Current.Children(name).Select(c => new ScopedNode(this, ParentResource, c, _fullUrl)); public string ShortPath => Current is ElementNode en ? en.ShortPath : Current.Location; diff --git a/src/Hl7.Fhir.Base/ElementModel/ScopedNodeExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/ScopedNodeExtensions.cs new file mode 100644 index 0000000000..551cb6a6b9 --- /dev/null +++ b/src/Hl7.Fhir.Base/ElementModel/ScopedNodeExtensions.cs @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017, 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 + */ + +using Hl7.Fhir.Model; +using Hl7.Fhir.Rest; +using Hl7.FhirPath.Sprache; +using System; +using System.Linq; + +#nullable enable + +namespace Hl7.Fhir.ElementModel +{ + /// + /// A set of functions on (and ) that + /// help resolving references from one resource to another. This includes both resolving + /// the reference within the resource (in en Bundle or contained resource) or reaching out + /// to an external resolver. + /// + public static class ScopedNodeExtensions + { + /// + /// Turn a relative reference into an absolute url, based on the fullUrl of the parent resource + /// + /// See https://www.hl7.org/fhir/bundle.html#references for more information + public static ResourceIdentity MakeAbsolute(this ScopedNode node, ResourceIdentity identity) + { + if (identity.IsRelativeRestUrl) + { + // Relocate the relative url on the base given in the fullUrl of the entry (if applicable) + var fullUrl = node.FullUrl(); + + if (fullUrl != null) + { + var parentIdentity = new ResourceIdentity(fullUrl); + + if (parentIdentity.IsAbsoluteRestUrl) + identity = identity.WithBase(parentIdentity.BaseUri); + else if (parentIdentity.IsUrn) + identity = new ResourceIdentity($"{parentIdentity}/{identity.Id}"); + } + + // Return the identity - will remain relative if we did not find a fullUrl + } + + return identity; + } + + /// + public static string MakeAbsolute(this ScopedNode node, string reference) => + node.MakeAbsolute(new ResourceIdentity(reference)).ToString(); + + /// + /// + /// + /// + /// + /// + /// + /// + public static T? Resolve(this T element, string reference, Func? externalResolver = null) where T : class, ITypedElement + { + // Then, resolve the url within the instance data first - this is only + // possible if we have a ScopedNode at hand + if (element is ScopedNode scopedNode) + { + // a special case for a reference to the container (in this parent) resource. + // It should make sure that we're only resolving to the first parent that actually contains resources + // of which the current element is a child. + if (reference == "#") + { + return (T?)(object?)locateContainer(scopedNode); + } + else + { + var identity = scopedNode.MakeAbsolute(new ResourceIdentity(reference)); + var result = locateLocalResource(identity); + if (result != null) return (T)(object)result; + } + } + + // Nothing found internally, now try the external resolver + return externalResolver != null ? externalResolver(reference) : null; + + ScopedNode? locateContainer(ScopedNode containee) + { + var scan = containee; + while (scan is not null) + { + if (scan.ParentResource is ScopedNode parent) + { + if (parent.ContainedResources().Any(cr => cr.Location == scan.Location)) return parent; + } + + scan = scan.ParentResource; + } + + return null; + } + + ScopedNode? locateLocalResource(ResourceIdentity identity) + { + var url = identity.ToString(); + + foreach (var parent in scopedNode.ParentResources()) + { + if (parent.InstanceType == FhirTypeNames.BUNDLE) + { + return ((ReferencedResourceCache)parent.BundledResources()).ResolveReference(url); // safe cast but we cannot change the signature + } + + if (parent.Id() == url) + return parent; + if (parent.ContainedResourcesWithId().ResolveReference(url) is { } resource) // safe cast but we cannot change the signature + return resource; + } + + return null; + } + } + + /// + /// Where this element is a Reference datatype, get the reference from it and resolve it. + /// + public static T? Resolve(this T element, Func? externalResolver = null) where T : class, ITypedElement + { + if (element is null) return default; + + // First, get the url to fetch from the focus + string? url = element switch + { + { Value: string s } => s, + { InstanceType: FhirTypeNames.REFERENCE } => element.ParseResourceReference()?.Reference, + _ => null + }; + + return url is not null ? Resolve(element, url, externalResolver) : default; + } + } +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.Conversions.cs b/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.Conversions.cs new file mode 100644 index 0000000000..1a05d2d145 --- /dev/null +++ b/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.Conversions.cs @@ -0,0 +1,137 @@ +using Hl7.Fhir.ElementModel.Adapters; +using Hl7.Fhir.Introspection; +using Hl7.Fhir.Model; +using Hl7.Fhir.Serialization; +using Hl7.Fhir.Specification; +using Hl7.Fhir.Utility; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; + +namespace Hl7.Fhir.ElementModel; + +public static partial class SourceNodeExtensions +{ + public static Base ToPoco(this ISourceNode source, ModelInspector inspector, Type pocoType = null, PocoBuilderSettings settings = null) => + new LegacyPocoBuilder(inspector, settings).BuildFrom(source, pocoType); + + public static T ToPoco(this ISourceNode source, ModelInspector inspector, PocoBuilderSettings settings = null) where T : Base => + (T)source.ToPoco(inspector, typeof(T), settings); + + /// + /// Turns the ISourceNode into a by adding type information to it. + /// + /// The node containing the source information. + /// The provider which supplies type information. + /// Optional. The type of the element at the root. + /// + /// An that represents the data in the node, with type information + /// added to it. + /// This extension method decorates the ISourceNode with a new instance of + /// an , passing on the parameters of this extension method. + /// + public static ITypedElement ToTypedElement(this ISourceNode node, IStructureDefinitionSummaryProvider provider, string type = null, TypedElementSettings settings = null) + => new TypedElementOnSourceNode(node, type, provider, settings: settings); + + /// + /// Adapting an ISourceNode to a without adding type information to it. + /// + /// + /// + /// In contrast to , + /// this method simulates an ITypedElement on top of an ISourceNode, without adding type information to + /// it. This is used internally in a few places in the API, where the component using the ITypedNode is aware it + /// cannot depend on type information being present, but should normally not be used. + /// + [Obsolete("WARNING! For internal API use only. Turning an untyped SourceNode into an ITypedElement without providing" + + "type information (see other overload) will cause side-effects with components in the API that are not prepared to deal with" + + "missing type information. Please don't use this overload unless you know what you are doing.")] + public static ITypedElement ToTypedElement(this ISourceNode node) => + new SourceNodeToTypedElementAdapter(node); + + + #region Xml + /// + /// Serializes an instance into a FHIR Xml string. + /// + /// The instance to serialize. + /// Formats and indents the serialized Xml. + /// Since has no type information, this function will throw unless + /// the originated from parsing using . + public static string ToXml(this ISourceNode source, bool pretty = false) + => SerializationUtil.WriteXmlToString(source.WriteTo, pretty); + + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task ToXmlAsync(this ISourceNode source, bool pretty = false) + => await SerializationUtil.WriteXmlToStringAsync(source.WriteToAsync, pretty).ConfigureAwait(false); + + /// + /// Serializes an instance into a . + /// + /// The instance to serialize. + /// Since has no type information, this function will throw unless + /// the originated from parsing using . + public static XDocument ToXDocument(this ISourceNode source) => + new FhirXmlBuilder().Build(source); + + /// + /// Serializes an instance into FHIR Xml. + /// + /// The instance to serialize. + /// The to write the serialized data to. + /// Since has no type information, this function will throw unless + /// the originated from parsing using . + public static void WriteTo(this ISourceNode source, XmlWriter writer) => + new FhirXmlBuilder().Build(source).writeTo(writer); + + /// + public static async Task WriteToAsync(this ISourceNode source, XmlWriter destination) => + await new FhirXmlBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); + #endregion + + #region Json + + /// + /// Serializes an instance into a FHIR Json string. + /// + /// The instance to serialize. + /// Formats and indents the serialized Json. + /// Since has no type information, this function will throw unless + /// the originated from parsing using . + public static string ToJson(this ISourceNode source, bool pretty = false) + => SerializationUtil.WriteJsonToString(source.WriteTo, pretty); + + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task ToJsonAsync(this ISourceNode source, bool pretty = false) + => await SerializationUtil.WriteJsonToStringAsync(source.WriteToAsync, pretty).ConfigureAwait(false); + + /// + /// Serializes an instance into a . + /// + /// The instance to serialize. + /// Since has no type information, this function will throw unless + /// the originated from parsing using . + public static JObject ToJObject(this ISourceNode source) => new FhirJsonBuilder().Build(source); + + /// + /// Serializes an instance into FHIR Json. + /// + /// The instance to serialize. + /// The to write the serialized data to. + /// Since has no type information, this function will throw unless + /// the originated from parsing using . + public static void WriteTo(this ISourceNode source, JsonWriter writer) => + new FhirJsonBuilder().Build(source).writeTo(writer); + + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task WriteToAsync(this ISourceNode source, JsonWriter destination) => + await new FhirJsonBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); + + #endregion +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.cs index dc941d5272..8be7d6da4b 100644 --- a/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.cs +++ b/src/Hl7.Fhir.Base/ElementModel/SourceNodeExtensions.cs @@ -13,212 +13,179 @@ using Hl7.Fhir.Utility; using Hl7.Fhir.Specification; using Hl7.Fhir.ElementModel.Adapters; +using Hl7.Fhir.Serialization; -namespace Hl7.Fhir.ElementModel +namespace Hl7.Fhir.ElementModel; + +public static partial class SourceNodeExtensions { - public static class SourceNodeExtensions - { - /// - /// Returns the direct children of a set of nodes. - /// - /// A list of nodes. - /// Optional.The name filter for the children. Can be omitted to not filter by name. - /// The children of all nodes passed into , aggregated into a single list. - public static IEnumerable Children(this IEnumerable node, string name = null) => - node == null ? throw Error.ArgumentNull(nameof(node)) : + /// + /// Returns the direct children of a set of nodes. + /// + /// A list of nodes. + /// Optional.The name filter for the children. Can be omitted to not filter by name. + /// The children of all nodes passed into , aggregated into a single list. + public static IEnumerable Children(this IEnumerable node, string name = null) => + node == null ? throw Error.ArgumentNull(nameof(node)) : node.SelectMany(n => n.Children(name)); - /// - /// Returns all descendants of a node. - /// - /// A node. - /// The descendants (children and by recursion all children of the children) of the node passed into - /// - public static IEnumerable Descendants(this ISourceNode node) - { - if (node == null) throw Error.ArgumentNull(nameof(node)); + /// + /// Returns all descendants of a node. + /// + /// A node. + /// The descendants (children and by recursion all children of the children) of the node passed into + /// + public static IEnumerable Descendants(this ISourceNode node) + { + if (node == null) throw Error.ArgumentNull(nameof(node)); - foreach (var child in node.Children()) - { - yield return child; - foreach (var grandchild in child.Descendants()) yield return grandchild; - } + foreach (var child in node.Children()) + { + yield return child; + foreach (var grandchild in child.Descendants()) yield return grandchild; } + } - /// - /// Returns all descendants of a set of nodes. - /// - /// A list of nodes. - /// The descendants (children and by recursion all children of the children) of the node passed into - /// - /// The descendants of all nodes passed into , aggregated into a single list. - public static IEnumerable Descendants(this IEnumerable nodes) => - nodes == null ? throw Error.ArgumentNull(nameof(nodes)) : + /// + /// Returns all descendants of a set of nodes. + /// + /// A list of nodes. + /// The descendants (children and by recursion all children of the children) of the node passed into + /// + /// The descendants of all nodes passed into , aggregated into a single list. + public static IEnumerable Descendants(this IEnumerable nodes) => + nodes == null ? throw Error.ArgumentNull(nameof(nodes)) : nodes.SelectMany(n => n.Descendants()); - /// - /// Returns a node and all descendants of that node. - /// - /// A node. - /// The node and descendants (children and by recursion all children of the children) of the node passed into - /// - public static IEnumerable DescendantsAndSelf(this ISourceNode node) - { - if (node == null) throw Error.ArgumentNull(nameof(node)); - return (new[] { node }).Concat(node.Descendants()); - } + /// + /// Returns a node and all descendants of that node. + /// + /// A node. + /// The node and descendants (children and by recursion all children of the children) of the node passed into + /// + public static IEnumerable DescendantsAndSelf(this ISourceNode node) + { + if (node == null) throw Error.ArgumentNull(nameof(node)); + return (new[] { node }).Concat(node.Descendants()); + } - /// - /// Returns nodes and all descendants of those nodes from a set of nodes. - /// - /// A list of nodes. - /// The node and descendants (children and by recursion all children of the children) of all - /// nodes passed into - public static IEnumerable DescendantsAndSelf(this IEnumerable nodes) - { - if (nodes == null) throw Error.ArgumentNull(nameof(nodes)); - return nodes.SelectMany(n => n.DescendantsAndSelf()); - } + /// + /// Returns nodes and all descendants of those nodes from a set of nodes. + /// + /// A list of nodes. + /// The node and descendants (children and by recursion all children of the children) of all + /// nodes passed into + public static IEnumerable DescendantsAndSelf(this IEnumerable nodes) + { + if (nodes == null) throw Error.ArgumentNull(nameof(nodes)); + return nodes.SelectMany(n => n.DescendantsAndSelf()); + } - /// - /// Runs an action on all nodes in a tree of nodes. - /// - /// The root of the tree. - /// The action to run on each node. - /// The function does a depth-first traversal of the tree, starting at the root. The action is - /// passed an integer representing the depth of the node in the tree, measured in steps from the node - /// to the root. - public static void Visit(this ISourceNode root, Action visitor) - { - if (root == null) throw Error.ArgumentNull(nameof(root)); - if (visitor == null) throw Error.ArgumentNull(nameof(visitor)); + /// + /// Runs an action on all nodes in a tree of nodes. + /// + /// The root of the tree. + /// The action to run on each node. + /// The function does a depth-first traversal of the tree, starting at the root. The action is + /// passed an integer representing the depth of the node in the tree, measured in steps from the node + /// to the root. + public static void Visit(this ISourceNode root, Action visitor) + { + if (root == null) throw Error.ArgumentNull(nameof(root)); + if (visitor == null) throw Error.ArgumentNull(nameof(visitor)); - root.visit(visitor, 0); - } + root.visit(visitor, 0); + } - private static void visit(this ISourceNode node, Action visitor, int depth = 0) + private static void visit(this ISourceNode node, Action visitor, int depth = 0) + { + visitor(depth, node); + foreach (var child in node.Children()) { - visitor(depth, node); - foreach (var child in node.Children()) - { - visit(child, visitor, depth + 1); - } + visit(child, visitor, depth + 1); } + } - /// - /// Registers an with an . - /// - /// - public static IDisposable Catch(this ISourceNode node, ExceptionNotificationHandler handler, bool forward = false) - { - if (node == null) throw Error.ArgumentNull(nameof(node)); - if (handler == null) throw Error.ArgumentNull(nameof(handler)); - - return node is IExceptionSource s ? - s.Catch(handler, forward) - : throw new NotImplementedException("Node does not implement IExceptionSource."); - } + /// + /// Registers an with an . + /// + /// + public static IDisposable Catch(this ISourceNode node, ExceptionNotificationHandler handler, bool forward = false) + { + if (node == null) throw Error.ArgumentNull(nameof(node)); + if (handler == null) throw Error.ArgumentNull(nameof(handler)); + return node is IExceptionSource s ? + s.Catch(handler, forward) + : throw new NotImplementedException("Node does not implement IExceptionSource."); + } - /// - /// Visit all nodes in a tree while invoking the getter. /> - /// - /// The root of the tree to visit. - /// Since implementations of ISourceNode will report parsing errors when enumerating - /// children and getting their getter, this will trigger all - /// parsing errors to be reported by the source. - public static void VisitAll(this ISourceNode root) - { - if (root == null) throw Error.ArgumentNull(nameof(root)); - root.Visit((_, n) => { var dummy = n.Text; }); - } - /// - /// Visits all nodes in a tree while catching any reported parsing errors. /> - /// - /// The root of the tree to visit. - /// The list of all exceptions reported while visiting the tree passed in - /// the argument. - /// - public static IList VisitAndCatch(this ISourceNode root) - { - if (root == null) throw Error.ArgumentNull(nameof(root)); + /// + /// Visit all nodes in a tree while invoking the getter. /> + /// + /// The root of the tree to visit. + /// Since implementations of ISourceNode will report parsing errors when enumerating + /// children and getting their getter, this will trigger all + /// parsing errors to be reported by the source. + public static void VisitAll(this ISourceNode root) + { + if (root == null) throw Error.ArgumentNull(nameof(root)); + root.Visit((_, n) => { var dummy = n.Text; }); + } - var errors = new List(); + /// + /// Visits all nodes in a tree while catching any reported parsing errors. /> + /// + /// The root of the tree to visit. + /// The list of all exceptions reported while visiting the tree passed in + /// the argument. + /// + public static IList VisitAndCatch(this ISourceNode root) + { + if (root == null) throw Error.ArgumentNull(nameof(root)); - using (root.Catch((o, arg) => errors.Add(arg))) - { - root.VisitAll(); - } + var errors = new List(); - return errors; + using (root.Catch((o, arg) => errors.Add(arg))) + { + root.VisitAll(); } + return errors; + } + - /// - /// Gets the annotation from an ISourceNode - /// - /// - public static string GetResourceTypeIndicator(this ISourceNode node) => - (node as IAnnotated)?.Annotation()?.ResourceType; - - /// - /// Gets specific annotations from the list of annotations on the node. - /// - /// All of the annotations of the given type, or an empty list if none were found. - /// - public static IEnumerable Annotations(this ISourceNode node, Type type) => - node is IAnnotated ann ? ann.Annotations(type) : Enumerable.Empty(); + /// + /// Gets the annotation from an ISourceNode + /// + /// + public static string GetResourceTypeIndicator(this ISourceNode node) => + (node as IAnnotated)?.Annotation()?.ResourceType; + + /// + /// Gets specific annotations from the list of annotations on the node. + /// + /// All of the annotations of the given type, or an empty list if none were found. + /// + public static IEnumerable Annotations(this ISourceNode node, Type type) => + node is IAnnotated ann ? ann.Annotations(type) : Enumerable.Empty(); - /// - /// Gets specific annotations from the list of annotations on the node. - /// - /// All of the annotations of the given type, or an empty list if none were found. - /// - public static IEnumerable Annotations(this ISourceNode node) => - (node is IAnnotated ann ? ann.Annotations() : []); - - /// - /// Gets a specific annotation from the list of annotations on the node. - /// - /// The first of the annotations of the type given by , - /// or an empty list if none were found. - /// - public static T Annotation(this ISourceNode nav) where T : class => - nav is IAnnotated ann ? ann.Annotation() : null; - - - /// - /// Turns the ISourceNode into a by adding type information to it. - /// - /// The node containing the source information. - /// The provider which supplies type information. - /// Optional. The type of the element at the root. - /// - /// An that represents the data in the node, with type information - /// added to it. - /// This extension method decorates the ISourceNode with a new instance of - /// an , passing on the parameters of this extension method. - /// - public static ITypedElement ToTypedElement(this ISourceNode node, IStructureDefinitionSummaryProvider provider, string type = null, TypedElementSettings settings = null) - => new TypedElementOnSourceNode(node, type, provider, settings: settings); - - - /// - /// Adapting an ISourceNode to a without adding type information to it. - /// - /// - /// - /// In contrast to , - /// this method simulates an ITypedElement on top of an ISourceNode, without adding type information to - /// it. This is used internally in a few places in the API, where the component using the ITypedNode is aware it - /// cannot depend on type information being present, but should normally not be used. - /// - [Obsolete("WARNING! For internal API use only. Turning an untyped SourceNode into an ITypedElement without providing" + -"type information (see other overload) will cause side-effects with components in the API that are not prepared to deal with" + -"missing type information. Please don't use this overload unless you know what you are doing.")] - public static ITypedElement ToTypedElement(this ISourceNode node) => - new SourceNodeToTypedElementAdapter(node); - } + /// + /// Gets specific annotations from the list of annotations on the node. + /// + /// All of the annotations of the given type, or an empty list if none were found. + /// + public static IEnumerable Annotations(this ISourceNode node) => + (node is IAnnotated ann ? ann.Annotations() : []); + + /// + /// Gets a specific annotation from the list of annotations on the node. + /// + /// The first of the annotations of the type given by , + /// or an empty list if none were found. + /// + public static T Annotation(this ISourceNode nav) where T : class => + nav is IAnnotated ann ? ann.Annotation() : null; } \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Serialization/FhirJsonBuilderExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.Conversions.cs similarity index 50% rename from src/Hl7.Fhir.Base/Serialization/FhirJsonBuilderExtensions.cs rename to src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.Conversions.cs index c617cf0f8f..df65afac47 100644 --- a/src/Hl7.Fhir.Base/Serialization/FhirJsonBuilderExtensions.cs +++ b/src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.Conversions.cs @@ -1,61 +1,45 @@ -/* - * Copyright (c) 2018, 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://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE - */ - - -using Hl7.Fhir.ElementModel; +using Hl7.Fhir.ElementModel.Adapters; using Hl7.Fhir.Introspection; using Hl7.Fhir.Model; +using Hl7.Fhir.Serialization; using Hl7.Fhir.Utility; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; -using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; + +namespace Hl7.Fhir.ElementModel; -namespace Hl7.Fhir.Serialization; +#nullable enable -public static class FhirJsonBuilderExtensions +public static partial class TypedElementExtensions { - /// - /// Serializes an instance to FHIR Json. - /// - /// The instance to serialize. - /// The to write the serialized data to. - public static void WriteTo(this ITypedElement source, JsonWriter writer) => - new FhirJsonBuilder().Build(source).writeTo(writer); + public static ScopedNode ToScopedNode(this ITypedElement node) => + node as ScopedNode ?? new ScopedNode(node); - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task WriteToAsync(this ITypedElement source, JsonWriter destination) => - await new FhirJsonBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); + internal static IEnumerable ToScopedNodes(this IEnumerable nodes) => + nodes.Select(n => n.ToScopedNode()); + + public static ISourceNode ToSourceNode(this ITypedElement node) => new TypedElementToSourceNodeAdapter(node); + + public static T ToPoco(this ITypedElement element, ModelInspector inspector, PocoBuilderSettings? settings = null) where T : Base => + (T)new NewPocoBuilder(inspector, settings ?? new PocoBuilderSettings()).BuildFrom(element); - /// - /// Serializes an instance into FHIR Json. - /// - /// The instance to serialize. - /// The to write the serialized data to. - /// Since has no type information, this function will throw unless - /// the originated from parsing using . - public static void WriteTo(this ISourceNode source, JsonWriter writer) => - new FhirJsonBuilder().Build(source).writeTo(writer); - - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task WriteToAsync(this ISourceNode source, JsonWriter destination) => - await new FhirJsonBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); + public static Base ToPoco(this ITypedElement element, ModelInspector inspector, PocoBuilderSettings? settings = null) => + new NewPocoBuilder(inspector, settings ?? new PocoBuilderSettings()).BuildFrom(element); /// - /// Serializes an instance into a . + /// Converts a typed element to a PocoNode. /// - /// The instance to serialize. - /// Since has no type information, this function will throw unless - /// the originated from parsing using . - public static JObject ToJObject(this ISourceNode source) => new FhirJsonBuilder().Build(source); + /// Will produce significantly more accurate results if a modelinspector is provided, or if the input is already a PocoNode + public static PocoNode ToPocoNode(this ITypedElement node, ModelInspector? inspector = null) => + node as PocoNode ?? node.ToPoco(inspector ?? node.Annotation() ?? ModelInspector.Base, new PocoBuilderSettings{IgnoreUnknownMembers = true}).ToPocoNode(); + + #region Json /// /// Serializes an instance into a @@ -92,21 +76,6 @@ public static async Task ToJsonAsync(this ITypedElement source, bool pre return ser.SerializeToString(resource, pretty); } - /// - /// Serializes an instance into a FHIR Json string. - /// - /// The instance to serialize. - /// Formats and indents the serialized Json. - /// Since has no type information, this function will throw unless - /// the originated from parsing using . - public static string ToJson(this ISourceNode source, bool pretty = false) - => SerializationUtil.WriteJsonToString(source.WriteTo, pretty); - - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task ToJsonAsync(this ISourceNode source, bool pretty = false) - => await SerializationUtil.WriteJsonToStringAsync(source.WriteToAsync, pretty).ConfigureAwait(false); - /// /// Serializes an instance into a FHIR Json byte array. /// @@ -119,16 +88,79 @@ public static byte[] ToJsonBytes(this ITypedElement source, bool pretty = false) [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] public static async Task ToJsonBytesAsync(this ITypedElement source, bool pretty = false) => await SerializationUtil.WriteJsonToBytesAsync(source.WriteToAsync, pretty).ConfigureAwait(false); + + /// + /// Serializes an instance to FHIR Json. + /// + /// The instance to serialize. + /// The to write the serialized data to. + public static void WriteTo(this ITypedElement source, JsonWriter writer) => + new FhirJsonBuilder().Build(source).writeTo(writer); + + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task WriteToAsync(this ITypedElement source, JsonWriter destination) => + await new FhirJsonBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); + + #endregion + + #region Xml + + /// + /// Serializes an instance into a FHIR Xml byte array. + /// + /// The instance to serialize. + /// Formats and indents the serialized Xml. + public static byte[] ToXmlBytes(this ITypedElement source, bool pretty = false) + => SerializationUtil.WriteXmlToBytes(source.WriteTo, pretty); + + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task ToXmlBytesAsync(this ITypedElement source, bool pretty = false) + => await SerializationUtil.WriteXmlToBytesAsync(source.WriteToAsync, pretty).ConfigureAwait(false); + + /// + /// Serializes an instance into a + /// + /// The instance to serialize. + public static XDocument ToXDocument(this ITypedElement source) => + new FhirXmlBuilder().Build(source); - private static void writeTo(this JObject root, JsonWriter destination) + /// + /// Serializes an instance into a FHIR Xml string. + /// + /// The instance to serialize. + /// Formats and indents the serialized Xml. + public static string ToXml(this ITypedElement source, bool pretty = false) { - root.WriteTo(destination); - destination.Flush(); + if (source is not PocoNode node) + return SerializationUtil.WriteXmlToString(source.WriteTo, pretty); + + return node.SerializeToString(pretty); } + + /// + /// Serializes an instance to FHIR Xml. + /// + /// The instance to serialize. + /// The to write the serialized data to. + public static void WriteTo(this ITypedElement source, XmlWriter writer) => + new FhirXmlBuilder().Build(source).writeTo(writer); + + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task WriteToAsync(this ITypedElement source, XmlWriter destination) => + await new FhirXmlBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); - private static async Task writeToAsync(this JObject root, JsonWriter destination) + /// + [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] + public static async Task ToXmlAsync(this ITypedElement source, bool pretty = false) { - await root.WriteToAsync(destination).ConfigureAwait(false); - await destination.FlushAsync().ConfigureAwait(false); + if (source is not PocoNode node) + return await SerializationUtil.WriteXmlToStringAsync(source.WriteToAsync, pretty).ConfigureAwait(false); + + return node.SerializeToString(pretty); } + + #endregion } \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.cs b/src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.cs index c031b96cae..2ac83a7dee 100644 --- a/src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.cs +++ b/src/Hl7.Fhir.Base/ElementModel/TypedElementExtensions.cs @@ -10,151 +10,193 @@ using EM=Hl7.Fhir.ElementModel.Types; -using Hl7.Fhir.Introspection; -using Hl7.Fhir.Model; +using Hl7.Fhir.Specification; using Hl7.Fhir.Utility; using System; -using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; -namespace Hl7.Fhir.ElementModel +namespace Hl7.Fhir.ElementModel; + +public static partial class TypedElementExtensions { - public static class TypedElementExtensions - { - /// - /// Converts a Poco to an ITypedElement. - /// - /// The Poco that should be converted to an . - /// The containing the POCO classes to be used for deserialization. - /// - /// - public static ITypedElement ToTypedElementLegacy(this Base @base, ModelInspector modelInspector, string? rootName = null) - => new PocoElementNode(modelInspector, @base, rootName: rootName); - - - /// - /// Creates an adapter which implements ITypedElement on top of a POCO instance, with explicit version-specific metadata. - /// - /// The POCO instance - /// The ModelInspector instance supplying version-specific metadata for the instance - /// The name you wish to have at the root of the tree. This will determine e.g. the root element name for serialization. - /// If none is given, the type of the underlying poco will be used. - /// The implementation of this method has changed. If you notice regressions, please let the SDK team know. - /// In the meantime, you can restore the old behaviour with a call to -#if NETSTANDARD2_1 - [Obsolete("The implementation of this method has changed to use our new model stack. If you want to try the new behaviour, "+ - "either ignore this warning or call ToPocoNode(). For reverting to the old behaviour, call .ToTypedElementLegacy()")] -#else - [Experimental("SDK0001")] -#endif - public static ITypedElement ToTypedElement(this Base @base, ModelInspector inspector, string? rootName = null) => - @base.ToPocoNode(inspector, rootName); - - /// - /// Converts a Poco to a new PocoElementNode. - /// - /// The Poco that should be converted to an . - /// An optional that should be used to access metadata about the resource. - /// An optional nome for the node at the root of the tree. - public static PocoNode ToPocoNode(this Base @base, ModelInspector? inspector = null, string? rootName = null) + public static IEnumerable Children(this IEnumerable nodes, string? name = null) => + nodes.SelectMany(n => n.Children(name)); + + public static IEnumerable Descendants(this ITypedElement element) { - var result = PocoNodeOrList.Root(@base, rootName); - if(inspector is not null) - ((IAnnotatable)result).AddAnnotation(inspector); + foreach (var child in element.Children()) + { + yield return child; - return result; + foreach (var grandchild in child.Descendants()) + { + yield return grandchild; + } + } } - /// - /// Determines whether the specified ITypedElement is equal to the current ITypedElement. You can discard the order of the elements - /// by setting the to true. - /// - /// The current to use in the equation. - /// The to compare with the current ITyoedElement. - /// When true the order of the children is discarded. When false the order of children is part - /// of the equation. - /// true when the ITypedElements are equal, false otherwise. -#pragma warning disable CS0618 // Type or member is obsolete - public static bool IsExactlyEqualTo(this ITypedElement? left, ITypedElement? right, bool ignoreOrder = false) -#pragma warning restore CS0618 // Type or member is obsolete + public static IEnumerable Descendants(this IEnumerable elements) => + elements.SelectMany(e => e.Descendants()); + + + public static IEnumerable DescendantsAndSelf(this ITypedElement element) => + (new[] { element }).Concat(element.Descendants()); + + public static IEnumerable DescendantsAndSelf(this IEnumerable elements) => + elements.SelectMany(e => e.DescendantsAndSelf()); + + public static void Visit(this ITypedElement root, Action visitor) => root.visit(visitor, 0); + + private static void visit(this ITypedElement root, Action visitor, int depth = 0) { - if (left == null && right == null) return true; - if (left == null || right == null) return false; + visitor(depth, root); - if (!ValueEquality(left.Value, right.Value)) return false; + foreach (var child in root.Children()) + { + visit(child, visitor, depth + 1); + } + } - // Compare the children. - var childrenL = left.Children(); - var childrenR = right.Children(); + public static IDisposable Catch(this ITypedElement source, ExceptionNotificationHandler handler) => + source is IExceptionSource s ? s.Catch(handler) : throw new NotImplementedException("Element does not implement IExceptionSource."); - if (childrenL.Count() != childrenR.Count()) - return false; + public static void VisitAll(this ITypedElement nav) => nav.Visit((_, n) => + { + var dummyValue = n.Value; + var dummyDefinition = n.Definition; + }); + + public static List VisitAndCatch(this ITypedElement node) + { + var errors = new List(); - if (ignoreOrder) + using (node.Catch((o, arg) => errors.Add(arg))) { - childrenL = childrenL.OrderBy(x => x.Name).ToList(); - childrenR = childrenR.OrderBy(x => x.Name).ToList(); + node.VisitAll(); } - return childrenL.Zip(childrenR, - (childL, childR) => childL.Name == childR.Name && childL.IsExactlyEqualTo(childR, ignoreOrder)).All(t => t); + return errors; } - /// - /// Determines whether the generic values and are equal. - /// - /// - /// - /// - /// - /// - public static bool ValueEquality(T1? val1, T2? val2) - { - // Compare the value - if (val1 is null && val2 is null) return true; - if (val1 is null || val2 is null) return false; - try + + public static IEnumerable Annotations(this ITypedElement nav, Type type) => + nav is IAnnotated ann ? ann.Annotations(type) : Enumerable.Empty(); + public static T? Annotation(this ITypedElement nav) => + nav is IAnnotated ann ? ann.Annotation() : default; + + public static IReadOnlyCollection ChildDefinitions(this ITypedElement me, + IStructureDefinitionSummaryProvider provider) + { + if (me.Definition != null) { - if (EM.Any.TryConvert(val1, out var lAny) && EM.Any.TryConvert(val2, out var rAny)) - { - return lAny is EM.ICqlEquatable cqle && cqle.IsEqualTo(rAny!) == true; - } + // If this is a backbone element, the child type is the nested complex type + if (me.Definition.Type[0] is IStructureDefinitionSummary be) + return be.GetElements(); else { - return false; + if (me.InstanceType != null) + { + var si = provider.Provide(me.InstanceType); + if (si != null) return si.GetElements(); + } } + + } + + // Note: fall-through in all failure cases - return empty collection + return new List(); + } + + /// + /// Determines whether the specified ITypedElement is equal to the current ITypedElement. You can discard the order of the elements + /// by setting the to true. + /// + /// The current to use in the equation. + /// The to compare with the current ITyoedElement. + /// When true the order of the children is discarded. When false the order of children is part + /// of the equation. + /// true when the ITypedElements are equal, false otherwise. +#pragma warning disable CS0618 // Type or member is obsolete + public static bool IsExactlyEqualTo(this ITypedElement? left, ITypedElement? right, bool ignoreOrder = false) +#pragma warning restore CS0618 // Type or member is obsolete + { + if (left == null && right == null) return true; + if (left == null || right == null) return false; + + if (!ValueEquality(left.Value, right.Value)) return false; + + // Compare the children. + var childrenL = left.Children(); + var childrenR = right.Children(); + + if (childrenL.Count() != childrenR.Count()) + return false; + + if (ignoreOrder) + { + childrenL = childrenL.OrderBy(x => x.Name).ToList(); + childrenR = childrenR.OrderBy(x => x.Name).ToList(); + } + + return childrenL.Zip(childrenR, + (childL, childR) => childL.Name == childR.Name && childL.IsExactlyEqualTo(childR, ignoreOrder)).All(t => t); + } + + /// + /// Determines whether the generic values and are equal. + /// + /// + /// + /// + /// + /// + public static bool ValueEquality(T1? val1, T2? val2) + { + // Compare the value + if (val1 is null && val2 is null) return true; + if (val1 is null || val2 is null) return false; + + try + { + if (EM.Any.TryConvert(val1, out var lAny) && EM.Any.TryConvert(val2, out var rAny)) + { + return lAny is EM.ICqlEquatable cqle && cqle.IsEqualTo(rAny!) == true; } - catch + else { return false; } } + catch + { + return false; + } + } - /// - /// Determines whether a matches a certain pattern. - /// - /// - /// - /// true when matches the , false otherwise. + /// + /// Determines whether a matches a certain pattern. + /// + /// + /// + /// true when matches the , false otherwise. #pragma warning disable CS0618 // Type or member is obsolete - public static bool Matches(this ITypedElement value, ITypedElement pattern) + public static bool Matches(this ITypedElement value, ITypedElement pattern) #pragma warning restore CS0618 // Type or member is obsolete - { - if (value == null && pattern == null) return true; - if (value == null || pattern == null) return false; + { + if (value == null && pattern == null) return true; + if (value == null || pattern == null) return false; - if (!ValueEquality(value.Value, pattern.Value)) return false; + if (!ValueEquality(value.Value, pattern.Value)) return false; - // Compare the children. - var valueChildren = value.Children(); - var patternChildren = pattern.Children(); + // Compare the children. + var valueChildren = value.Children(); + var patternChildren = pattern.Children(); - return patternChildren.All(patternChild => valueChildren.Any(valueChild => - patternChild.Name == valueChild.Name && valueChild.Matches(patternChild))); + return patternChildren.All(patternChild => valueChildren.Any(valueChild => + patternChild.Name == valueChild.Name && valueChild.Matches(patternChild))); - } } } #nullable restore \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/FhirPath/CompiledExpression.cs b/src/Hl7.Fhir.Base/FhirPath/CompiledExpression.cs index 40e9df0dd3..83a31d152d 100644 --- a/src/Hl7.Fhir.Base/FhirPath/CompiledExpression.cs +++ b/src/Hl7.Fhir.Base/FhirPath/CompiledExpression.cs @@ -3,12 +3,14 @@ using Hl7.Fhir.ElementModel; using Hl7.Fhir.Model; using Hl7.FhirPath.Functions; +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Hl7.FhirPath { - public delegate IEnumerable CompiledExpression(IScopedNode root, EvaluationContext ctx); + public delegate IEnumerable CompiledExpression(PocoNode root, EvaluationContext ctx); public static class CompiledExpressionExtensions { @@ -19,10 +21,10 @@ public static class CompiledExpressionExtensions /// Input at which the expression is evaluated /// Context of the evaluation /// The single result of an expression - public static object? Scalar(this CompiledExpression evaluator, IScopedNode input, EvaluationContext ctx) + public static object? Scalar(this CompiledExpression evaluator, PocoNode input, EvaluationContext ctx) { var result = evaluator(input, ctx).Take(2).ToArray(); - return result.Any() ? result.Single().Value : null; + return result.SingleOrDefault()?.GetValue(); } /// @@ -32,13 +34,13 @@ public static class CompiledExpressionExtensions /// Input at which the expression is evaluated /// Context of the evaluation /// True if expression returns true of empty, otheriwse false - public static bool Predicate(this CompiledExpression evaluator, IScopedNode input, EvaluationContext ctx) + public static bool Predicate(this CompiledExpression evaluator, PocoNode input, EvaluationContext ctx) { var result = evaluator(input, ctx).BooleanEval(); return result is null || result.Value; } - /// + /// public static bool Predicate(this CompiledExpression evaluator, Base input, EvaluationContext ctx) { var result = evaluator(input.ToPocoNode(), ctx).BooleanEval(); @@ -52,7 +54,7 @@ public static bool Predicate(this CompiledExpression evaluator, Base input, Eval /// Input at which the expression is evaluated /// Context of the evaluation /// True if expression returns true , and false if expression returns empty of false. - public static bool IsTrue(this CompiledExpression evaluator, IScopedNode input, EvaluationContext ctx) + public static bool IsTrue(this CompiledExpression evaluator, PocoNode input, EvaluationContext ctx) { var result = evaluator(input, ctx).BooleanEval(); return result is not null && result.Value; @@ -67,7 +69,7 @@ public static bool IsTrue(this CompiledExpression evaluator, IScopedNode input, /// Input at which the expression is evaluated /// Context of the evaluation /// True if the result of an expression is equal to a given boolean, otherwise false - public static bool IsBoolean(this CompiledExpression evaluator, bool value, IScopedNode input, EvaluationContext ctx) + public static bool IsBoolean(this CompiledExpression evaluator, bool value, PocoNode input, EvaluationContext ctx) { var result = evaluator(input, ctx).BooleanEval(); return result is not null && result.Value == value; diff --git a/src/Hl7.Fhir.Base/FhirPath/ElementNavFhirExtensions.cs b/src/Hl7.Fhir.Base/FhirPath/ElementNavFhirExtensions.cs index 66a8f15c8b..5b66831ed5 100644 --- a/src/Hl7.Fhir.Base/FhirPath/ElementNavFhirExtensions.cs +++ b/src/Hl7.Fhir.Base/FhirPath/ElementNavFhirExtensions.cs @@ -39,16 +39,16 @@ public static void PrepareFhirSymbolTableFunctions() public static SymbolTable AddFhirExtensions(this SymbolTable t) { - t.Add("hasValue", (IScopedNode f) => f.HasValue(), doNullProp: false); - t.Add("resolve", (IScopedNode f, EvaluationContext ctx) => resolver(f, ctx), doNullProp: false); - t.Add("resolve", (IEnumerable f, EvaluationContext ctx) => f.Select(fi => resolver(fi, ctx)), doNullProp: false); + t.Add("hasValue", (PocoNode f) => f.HasValue(), doNullProp: false); + t.Add("resolve", (PocoNode f, EvaluationContext ctx) => resolver(f, ctx), doNullProp: false); + t.Add("resolve", (IEnumerable f, EvaluationContext ctx) => f.Select(fi => resolver(fi, ctx)), doNullProp: false); - t.Add("memberOf", (IScopedNode input, string valueset, EvaluationContext ctx) => MemberOf(input, valueset, ctx), doNullProp: false); + t.Add("memberOf", (PocoNode input, string valueset, EvaluationContext ctx) => MemberOf(input, valueset, ctx), doNullProp: false); // Pre-normative this function was called htmlchecks, normative is htmlChecks // lets keep both to keep everyone happy. - t.Add("htmlchecks", (IScopedNode f) => f.HtmlChecks(), doNullProp: false); - t.Add("htmlChecks", (IScopedNode f) => f.HtmlChecks(), doNullProp: false); + t.Add("htmlchecks", (PocoNode f) => f.HtmlChecks(), doNullProp: false); + t.Add("htmlChecks", (PocoNode f) => f.HtmlChecks(), doNullProp: false); t.Add("lowBoundary", (decimal d, long precision) => AdjustBoundaryDecimal(d, precision, substract), doNullProp: false); t.Add("lowBoundary", (decimal d) => AdjustBoundaryDecimal(d, null, substract), doNullProp: false); @@ -69,7 +69,7 @@ public static SymbolTable AddFhirExtensions(this SymbolTable t) return t; - static IScopedNode? resolver(IScopedNode f, EvaluationContext ctx) + static PocoNode? resolver(PocoNode f, EvaluationContext ctx) { return ctx is FhirEvaluationContext fctx ? f.Resolve(fctx.ElementResolver) : f.Resolve(); } @@ -81,47 +81,18 @@ public static SymbolTable AddFhirExtensions(this SymbolTable t) /// /// /// - public static bool HasValue(this IScopedNode focus) => focus?.Value is not null; + public static bool HasValue(this PocoNode focus) => focus is PrimitiveNode {Value: not null}; /// - /// Check if the node has a value, and not just extensions. + /// Check if the node has a valid Xhtml narrative value, and not just extensions. /// /// /// - public static bool HtmlChecks(this IScopedNode focus) - { - if (focus?.Value is null) return false; + public static bool HtmlChecks(this PocoNode focus) => focus is PrimitiveNode {Primitive: XHtml xhtml} && XHtml.IsValidNarrativeXhtml(xhtml.Value); - // Perform the checking of the content for valid html content - return XHtml.IsValidNarrativeXhtml(focus.Value.ToString()!); - } - - public static IEnumerable ToFhirValues(this IEnumerable results) + public static IEnumerable ToFhirValues(this IEnumerable results) { - return results.Select(r => - { - if (r is null) - return null; - - var fhirValue = r.Annotation() ?? r as IFhirValueProvider; - if (fhirValue != null) - { - return fhirValue.FhirValue; - } - - return r.Value switch - { - bool b => new FhirBoolean(b), - long l => new Integer64(l), - int i => new Integer(i), - decimal dec => new FhirDecimal(dec), - string s => new FhirString(s), - P.Date d => new Date(d.ToString()), - P.Time t => new Time(t.ToString()), - P.DateTime dt => new FhirDateTime(dt.ToDateTimeOffset(TimeSpan.Zero).ToUniversalTime()), - var other => (Base?)other - }; - }); + return results.Select(r => r.Poco); } @@ -268,7 +239,7 @@ internal static P.Time BoundaryTime(P.Time time, long? precision, int minutes, i /// EvaluationContext of the FhirPath compiler /// See summary /// - internal static bool? MemberOf(IScopedNode input, string valueset, EvaluationContext ctx) + internal static bool? MemberOf(PocoNode input, string valueset, EvaluationContext ctx) { var service = (ctx is FhirEvaluationContext fctx ? fctx.TerminologyService : null) ?? throw new ArgumentNullException(nameof(ctx), "The 'memberOf' function cannot be executed because the FhirEvaluationContext does not include a TerminologyService."); @@ -276,12 +247,12 @@ internal static P.Time BoundaryTime(P.Time time, long? precision, int minutes, i ValidateCodeParameters? inParams = new ValidateCodeParameters() .WithValueSet(valueset); - inParams = input.InstanceType switch + inParams = input switch { - "code" => inParams.WithCode(code: input.Value as string, context: input.GetLocalLocation()), - "Coding" => inParams.WithCoding(input.ParseCoding()), - "CodeableConcept" => inParams.WithCodeableConcept(input.ParseCodeableConcept()), - "string" or "System.String" => inParams.WithCode(code: input.Value as string, context: "No context available"), + { Poco: Code code } => inParams.WithCode(code: code.Value, context: input.GetLocalLocation()), + { Poco: Coding coding } => inParams.WithCoding(coding), + { Poco: CodeableConcept cc } => inParams.WithCodeableConcept(cc), + PrimitiveNode { Value: string s } => inParams.WithCode(code: s, context: "No context available"), _ => null, }; diff --git a/src/Hl7.Fhir.Base/FhirPath/EvaluationContext.cs b/src/Hl7.Fhir.Base/FhirPath/EvaluationContext.cs index b0d0a5ab05..d6b243c765 100644 --- a/src/Hl7.Fhir.Base/FhirPath/EvaluationContext.cs +++ b/src/Hl7.Fhir.Base/FhirPath/EvaluationContext.cs @@ -23,7 +23,7 @@ public EvaluationContext() /// /// The data that will be represented by %resource [Obsolete("%resource and %rootResource are inferred from scoped nodes by the evaluator. If you do not have access to a scoped node, or if you wish to explicitly override this behaviour, use the EvaluationContext.WithResourceOverrides() method.")] - public EvaluationContext(IScopedNode? resource) : this(resource, null) { } + public EvaluationContext(PocoNode? resource) : this(resource, null) { } /// /// Create an EvaluationContext with the given value for %resource and %rootResource. @@ -31,14 +31,14 @@ public EvaluationContext(IScopedNode? resource) : this(resource, null) { } /// The data that will be represented by %resource. /// The data that will be represented by %rootResource. [Obsolete("%resource and %rootResource are inferred from scoped nodes by the evaluator. If you do not have access to a scoped node, or if you wish to explicitly override this behaviour, use the EvaluationContext.WithResourceOverrides() method.")] - public EvaluationContext(IScopedNode? resource, IScopedNode? rootResource) + public EvaluationContext(PocoNode? resource, PocoNode? rootResource) { Resource = resource; RootResource = rootResource ?? resource; } [Obsolete("%resource and %rootResource are inferred from scoped nodes by the evaluator. If you do not have access to a scoped node, or if you wish to explicitly override this behaviour, use the EvaluationContext.WithResourceOverrides() method. Environment can be set explicitly after construction of the base context")] - public EvaluationContext(IScopedNode? resource, IScopedNode? rootResource, IDictionary> environment) : this(resource, rootResource) + public EvaluationContext(PocoNode? resource, PocoNode? rootResource, IDictionary> environment) : this(resource, rootResource) { Environment = environment; } @@ -46,27 +46,27 @@ public EvaluationContext(IScopedNode? resource, IScopedNode? rootResource, IDict /// /// The data represented by %rootResource. /// - public IScopedNode? RootResource { get; set; } + public PocoNode? RootResource { get; set; } /// /// The data represented by %resource. /// - public IScopedNode? Resource { get; set; } + public PocoNode? Resource { get; set; } /// /// The environment variables that are available to the FHIRPath expressions. /// - public IDictionary> Environment { get; set; } = new Dictionary>(); + public IDictionary> Environment { get; set; } = new Dictionary>(); /// /// A delegate that handles the output for the trace() function. /// - public Action>? Tracer { get; set; } + public Action>? Tracer { get; set; } } public static class EvaluationContextExtensions { - public static T WithResourceOverrides(this T context, IScopedNode? resource, IScopedNode? rootResource = null) where T : EvaluationContext + public static T WithResourceOverrides(this T context, PocoNode? resource, PocoNode? rootResource = null) where T : EvaluationContext { context.Resource = resource; context.RootResource = rootResource ?? resource; diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/Closure.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/Closure.cs index 2a238678d4..ea597aa719 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/Closure.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/Closure.cs @@ -10,6 +10,8 @@ using Hl7.Fhir.ElementModel; using Hl7.Fhir.Model; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Hl7.FhirPath.Expressions { @@ -21,7 +23,7 @@ public Closure() public EvaluationContext EvaluationContext { get; private set; } - public static Closure Root(IScopedNode root, EvaluationContext ctx = null) + public static Closure Root([NotNull] PocoNodeOrList root, EvaluationContext ctx = null) { var newContext = ctx ?? new EvaluationContext(); @@ -32,17 +34,15 @@ public static Closure Root(IScopedNode root, EvaluationContext ctx = null) var newClosure = new Closure() { EvaluationContext = ctx ?? new EvaluationContext() }; - var input = new[] { root }; - foreach (var assignment in newClosure.EvaluationContext.Environment) { newClosure.SetValue(assignment.Key, assignment.Value); } - newClosure.SetThis(input); - newClosure.SetThat(input); - newClosure.SetIndex(PocoNodeOrList.ForPrimitive(1)); - newClosure.SetOriginalContext(input); + newClosure.SetThis(root); + newClosure.SetThat(root); + newClosure.SetIndex(PocoNode.ForPrimitive(1)); + newClosure.SetOriginalContext(root); if (newContext.Resource != null) newClosure.SetResource(new[] { newContext.Resource }); if (newContext.RootResource != null) newClosure.SetRootResource(new[] { newContext.RootResource }); @@ -50,9 +50,9 @@ public static Closure Root(IScopedNode root, EvaluationContext ctx = null) return newClosure; } - private Dictionary> _namedValues = new (); + private Dictionary> _namedValues = new (); - public virtual void SetValue(string name, IEnumerable value) + public virtual void SetValue(string name, IEnumerable value) { _namedValues.Remove(name); _namedValues.Add(name, value); @@ -71,10 +71,10 @@ public virtual Closure Nest() } - public virtual IEnumerable ResolveValue(string name) + public virtual IEnumerable ResolveValue(string name) { // First, try to directly get "normal" values - _namedValues.TryGetValue(name, out IEnumerable result); + _namedValues.TryGetValue(name, out IEnumerable result); if (result != null) return result; diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/ClosureExtensions.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/ClosureExtensions.cs index b4fdc02353..98a62215f5 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/ClosureExtensions.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/ClosureExtensions.cs @@ -14,35 +14,35 @@ namespace Hl7.FhirPath.Expressions { internal static class ClosureExtensions { - public static IEnumerable GetThis(this Closure ctx) + public static IEnumerable GetThis(this Closure ctx) { return ctx.ResolveValue("builtin.this"); } - public static void SetThis(this Closure ctx, IEnumerable value) + public static void SetThis(this Closure ctx, IEnumerable value) { ctx.SetValue("builtin.this", value); } - public static IEnumerable GetTotal(this Closure ctx) + public static IEnumerable GetTotal(this Closure ctx) { return ctx.ResolveValue("builtin.total"); } - public static void SetTotal(this Closure ctx, IEnumerable value) + public static void SetTotal(this Closure ctx, IEnumerable value) { ctx.SetValue("builtin.total", value); } - public static IEnumerable GetThat(this Closure ctx) + public static IEnumerable GetThat(this Closure ctx) { return ctx.ResolveValue("builtin.that"); } - public static void SetThat(this Closure ctx, IEnumerable value) + public static void SetThat(this Closure ctx, IEnumerable value) { ctx.SetValue("builtin.that", value); } @@ -50,7 +50,7 @@ public static void SetThat(this Closure ctx, IEnumerable value) /// /// The original node that was passed to the evaluation engine before starting evaluation. /// - public static void SetOriginalContext(this Closure ctx, IEnumerable value) + public static void SetOriginalContext(this Closure ctx, IEnumerable value) { ctx.SetValue("context", value); } @@ -60,7 +60,7 @@ public static void SetOriginalContext(this Closure ctx, IEnumerable /// resource the element is part of. Do not go past a root resource into a bundle, if it is contained /// in a bundle. /// - public static void SetResource(this Closure ctx, IEnumerable value) + public static void SetResource(this Closure ctx, IEnumerable value) { ctx.SetValue("resource", value); } @@ -69,29 +69,29 @@ public static void SetResource(this Closure ctx, IEnumerable value) /// When a DomainResource contains another resource, and that contained resource is the focus (%resource) /// then %rootResource refers to the container resource. /// - public static void SetRootResource(this Closure ctx, IEnumerable value) + public static void SetRootResource(this Closure ctx, IEnumerable value) { ctx.SetValue("rootResource", value); } - public static IEnumerable GetOriginalContext(this Closure ctx) + public static IEnumerable GetOriginalContext(this Closure ctx) { return ctx.ResolveValue("context"); } - public static IEnumerable GetResource(this Closure ctx) + public static IEnumerable GetResource(this Closure ctx) { return ctx.ResolveValue("resource"); } - public static IEnumerable GetRootResource(this Closure ctx) + public static IEnumerable GetRootResource(this Closure ctx) { return ctx.ResolveValue("rootResource"); } - public static Closure Nest(this Closure ctx, IEnumerable input) + public static Closure Nest(this Closure ctx, IEnumerable input) { var nested = ctx.Nest(); nested.SetThat(input); @@ -99,12 +99,12 @@ public static Closure Nest(this Closure ctx, IEnumerable input) return nested; } - public static void SetIndex(this Closure ctx, IEnumerable value) + public static void SetIndex(this Closure ctx, IEnumerable value) { ctx.SetValue("builtin.index", value); } - public static IEnumerable GetIndex(this Closure ctx) + public static IEnumerable GetIndex(this Closure ctx) { return ctx.ResolveValue("builtin.index"); } diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/DynaDispatcher.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/DynaDispatcher.cs index f25112f166..ea2d6e0b68 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/DynaDispatcher.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/DynaDispatcher.cs @@ -26,9 +26,9 @@ public DynaDispatcher(string name, SymbolTable scope) private readonly string _name; private readonly SymbolTable _scope; - public IEnumerable Dispatcher(Closure context, IEnumerable args) + public IEnumerable Dispatcher(Closure context, IEnumerable args) { - var actualArgs = new List>(); + var actualArgs = new List>(); var focus = args.First()(context, InvokeeFactory.EmptyArgs); if (!focus.Any()) return []; diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/EvaluatorVisitor.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/EvaluatorVisitor.cs index f6d8b68ad4..1f62c23257 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/EvaluatorVisitor.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/EvaluatorVisitor.cs @@ -25,13 +25,9 @@ public EvaluatorVisitor(SymbolTable symbols) Symbols = symbols; } - - [TemporarilyChanged] - // we should investigate here. We will need a way to handle "object" without a fhir type, - // and try to infer it from the object. In the end we will remove the ToScopedNode. public override Invokee VisitConstant(FP.ConstantExpression expression) { - return InvokeeFactory.Return(ElementNode.ForPrimitive(expression.Value).ToScopedNode()); + return InvokeeFactory.Return(PocoNode.ForAnyPrimitive(expression.Value)); } public override Invokee VisitFunctionCall(FP.FunctionCallExpression expression) @@ -87,7 +83,7 @@ public override Invokee VisitVariableRef(FP.VariableRefExpression expression) return chainResolves; - IEnumerable chainResolves(Closure context, IEnumerable invokees) + IEnumerable chainResolves(Closure context, IEnumerable invokees) { return context.ResolveValue(expression.Name) ?? resolve(Symbols, expression.Name, Enumerable.Empty())(context, []); } @@ -135,4 +131,4 @@ public static Invokee ToEvaluator(this FP.Expression expr, SymbolTable scope) } } -} \ No newline at end of file +} diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/Invokee.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/Invokee.cs index c414fbeed8..7ec26a1c86 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/Invokee.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/Invokee.cs @@ -15,42 +15,42 @@ namespace Hl7.FhirPath.Expressions { - internal delegate IEnumerable Invokee(Closure context, IEnumerable arguments); + internal delegate IEnumerable Invokee(Closure context, IEnumerable arguments); internal static class InvokeeFactory { public static readonly IEnumerable EmptyArgs = Enumerable.Empty(); - public static IEnumerable GetThis(Closure context, IEnumerable _) + public static IEnumerable GetThis(Closure context, IEnumerable _) { return context.GetThis(); } - public static IEnumerable GetTotal(Closure context, IEnumerable _) + public static IEnumerable GetTotal(Closure context, IEnumerable _) { return context.GetTotal(); } - public static IEnumerable GetContext(Closure context, IEnumerable _) + public static IEnumerable GetContext(Closure context, IEnumerable _) { return context.GetOriginalContext(); } - public static IEnumerable GetResource(Closure context, IEnumerable _) + public static IEnumerable GetResource(Closure context, IEnumerable _) { return context.GetResource(); } - public static IEnumerable GetRootResource(Closure context, IEnumerable arguments) + public static IEnumerable GetRootResource(Closure context, IEnumerable arguments) { return context.GetRootResource(); } - public static IEnumerable GetThat(Closure context, IEnumerable _) + public static IEnumerable GetThat(Closure context, IEnumerable _) { return context.GetThat(); } - public static IEnumerable GetIndex(Closure context, IEnumerable args) + public static IEnumerable GetIndex(Closure context, IEnumerable args) { return context.GetIndex(); @@ -59,7 +59,7 @@ public static IEnumerable GetIndex(Closure context, IEnumerable(Func func) { - return (_, _) => Typecasts.CastTo>(func()); + return (_, _) => Typecasts.CastTo>(func()); } public static Invokee Wrap(Func func, bool propNull) @@ -71,12 +71,12 @@ public static Invokee Wrap(Func func, bool propNull) var focus = args.First()(ctx, InvokeeFactory.EmptyArgs); if (propNull && !focus.Any()) return []; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus))); + return Typecasts.CastTo>(func(Typecasts.CastTo(focus))); } else { A lastPar = (A)(object)ctx.EvaluationContext; - return Typecasts.CastTo>(func(lastPar)); + return Typecasts.CastTo>(func(lastPar)); } }; } @@ -105,12 +105,12 @@ public static Invokee Wrap(Func func, bool propNull) var argA = args.Skip(1).First()(ctx, InvokeeFactory.EmptyArgs); if (propNull && !argA.Any()) return []; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA))); + return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA))); } else { B lastPar = (B)(object)ctx.EvaluationContext; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus), lastPar)); + return Typecasts.CastTo>(func(Typecasts.CastTo(focus), lastPar)); } }; } @@ -130,13 +130,13 @@ public static Invokee Wrap(Func func, bool propNull) var argB = args.Skip(2).First()(ctx, InvokeeFactory.EmptyArgs); if (propNull && !argB.Any()) return []; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA), + return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA), Typecasts.CastTo(argB))); } else { C lastPar = (C)(object)ctx.EvaluationContext; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus), + return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA), lastPar)); } }; @@ -159,14 +159,14 @@ public static Invokee Wrap(Func func, bool propNul var argC = args.Skip(3).First()(ctx, InvokeeFactory.EmptyArgs); if (propNull && !argC.Any()) return []; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus), + return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA), Typecasts.CastTo(argB), Typecasts.CastTo(argC))); } else { D lastPar = (D)(object)ctx.EvaluationContext; - return Typecasts.CastTo>(func(Typecasts.CastTo(focus), + return Typecasts.CastTo>(func(Typecasts.CastTo(focus), Typecasts.CastTo(argA), Typecasts.CastTo(argB), lastPar)); } @@ -183,16 +183,16 @@ public static Invokee WrapLogic(Func, Func, bool?> func) var right = args.Skip(2).First(); // Return function that actually executes the Invokee at the last moment - return Typecasts.CastTo>(func(() => left(ctx, InvokeeFactory.EmptyArgs).BooleanEval(), () => right(ctx, InvokeeFactory.EmptyArgs).BooleanEval())); + return Typecasts.CastTo>(func(() => left(ctx, InvokeeFactory.EmptyArgs).BooleanEval(), () => right(ctx, InvokeeFactory.EmptyArgs).BooleanEval())); }; } - public static Invokee Return(IScopedNode value) + public static Invokee Return(PocoNode value) { return (_, _) => [value]; } - public static Invokee Return(IEnumerable value) + public static Invokee Return(IEnumerable value) { return (_, _) => value; } diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/SymbolTable.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/SymbolTable.cs index 605ea30070..4939f44cc8 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/SymbolTable.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/SymbolTable.cs @@ -160,10 +160,10 @@ public static void AddLogic(this SymbolTable table, string name, Func f) => f.Any()); t.Add("count", (IEnumerable f) => f.Count()); - t.Add("trace", (IEnumerable f, string name, EvaluationContext ctx) + t.Add("trace", (IEnumerable f, string name, EvaluationContext ctx) => f.Trace(name, ctx)); - t.Add("allTrue", (IEnumerable f) => f.All(e => e.Value as bool? == true)); - t.Add("anyTrue", (IEnumerable f) => f.Any(e => e.Value as bool? == true)); - t.Add("allFalse", (IEnumerable f) => f.All(e => e.Value as bool? == false)); - t.Add("anyFalse", (IEnumerable f) => f.Any(e => e.Value as bool? == false)); - t.Add("combine", (IEnumerable l, IEnumerable r) => l.Concat(r)); - t.Add("binary.|", (object _, IEnumerable l, IEnumerable r) => l.DistinctUnion(r)); - t.Add("union", (IEnumerable l, IEnumerable r) => l.DistinctUnion(r)); - t.Add("binary.contains", (object _, IEnumerable a, IScopedNode b) => a.Contains(b)); - t.Add("binary.in", (object _, IScopedNode a, IEnumerable b) => b.Contains(a)); - t.Add("distinct", (IEnumerable f) => f.Distinct()); - t.Add("isDistinct", (IEnumerable f) => f.IsDistinct()); - t.Add("subsetOf", (IEnumerable f, IEnumerable a) => f.SubsetOf(a)); - t.Add("supersetOf", (IEnumerable f, IEnumerable a) => a.SubsetOf(f)); - t.Add("intersect", (IEnumerable f, IEnumerable a) => f.Intersect(a)); - t.Add("exclude", (IEnumerable f, IEnumerable a) => f.Exclude(a)); + t.Add("allTrue", (IEnumerable f) => f.All(e => e.GetValue() is true)); + t.Add("anyTrue", (IEnumerable f) => f.Any(e => e.GetValue() is true)); + t.Add("allFalse", (IEnumerable f) => f.All(e => e.GetValue() is false)); + t.Add("anyFalse", (IEnumerable f) => f.Any(e => e.GetValue() is false)); + t.Add("combine", (IEnumerable l, IEnumerable r) => l.Concat(r)); + t.Add("binary.|", (object _, IEnumerable l, IEnumerable r) => l.DistinctUnion(r)); + t.Add("union", (IEnumerable l, IEnumerable r) => l.DistinctUnion(r)); + t.Add("binary.contains", (object _, IEnumerable a, PocoNode b) => a.Contains(b)); + t.Add("binary.in", (object _, PocoNode a, IEnumerable b) => b.Contains(a)); + t.Add("distinct", (IEnumerable f) => f.Distinct()); + t.Add("isDistinct", (IEnumerable f) => f.IsDistinct()); + t.Add("subsetOf", (IEnumerable f, IEnumerable a) => f.SubsetOf(a)); + t.Add("supersetOf", (IEnumerable f, IEnumerable a) => a.SubsetOf(f)); + t.Add("intersect", (IEnumerable f, IEnumerable a) => f.Intersect(a)); + t.Add("exclude", (IEnumerable f, IEnumerable a) => f.Exclude(a)); t.Add("today", (object _) => P.Date.Today()); t.Add("now", (object _) => P.DateTime.Now()); @@ -57,21 +57,21 @@ public static SymbolTable AddStandardFP(this SymbolTable t) t.Add("binary.&", (object _, string a, string b) => (a ?? "") + (b ?? "")); - t.Add(new CallSignature("iif", typeof(IEnumerable), typeof(object), typeof(bool?), typeof(Invokee), typeof(Invokee)), runIif); - t.Add(new CallSignature("iif", typeof(IEnumerable), typeof(object), typeof(bool?), typeof(Invokee)), runIif); + t.Add(new CallSignature("iif", typeof(IEnumerable), typeof(object), typeof(bool?), typeof(Invokee), typeof(Invokee)), runIif); + t.Add(new CallSignature("iif", typeof(IEnumerable), typeof(object), typeof(bool?), typeof(Invokee)), runIif); // Functions that use normal null propagation and work with the focus (buy may ignore it) - t.Add("not", (IEnumerable f) => f.Not(), doNullProp: true); - // t.Add("builtin.children", (IEnumerable f, string a) => f.Navigate(a), doNullProp: true); + t.Add("not", (IEnumerable f) => f.Not(), doNullProp: true); + // t.Add("builtin.children", (IEnumerable f, string a) => f.Navigate(a), doNullProp: true); t.AddBuiltinChildren(); - t.Add("children", (IEnumerable f) => f.Children(), doNullProp: true); - t.Add("descendants", (IEnumerable f) => f.Descendants().ToScopedNodes(), doNullProp: true); + t.Add("children", (IEnumerable f) => f.SelectMany(node => node.Children().SelectMany(n => n)), doNullProp: true); + t.Add("descendants", (IEnumerable f) => f.Descendants(), doNullProp: true); - t.Add("binary.=", (object f, IEnumerable a, IEnumerable b) => a.IsEqualTo(b), doNullProp: true); - t.Add("binary.!=", (object f, IEnumerable a, IEnumerable b) => !a.IsEqualTo(b), doNullProp: true); - t.Add("binary.~", (object f, IEnumerable a, IEnumerable b) => a.IsEquivalentTo(b), doNullProp: false); - t.Add("binary.!~", (object f, IEnumerable a, IEnumerable b) => !a.IsEquivalentTo(b), doNullProp: false); + t.Add("binary.=", (object f, IEnumerable a, IEnumerable b) => a.IsEqualTo(b), doNullProp: true); + t.Add("binary.!=", (object f, IEnumerable a, IEnumerable b) => !a.IsEqualTo(b), doNullProp: true); + t.Add("binary.~", (object f, IEnumerable a, IEnumerable b) => a.IsEquivalentTo(b), doNullProp: false); + t.Add("binary.!~", (object f, IEnumerable a, IEnumerable b) => !a.IsEquivalentTo(b), doNullProp: false); t.Add("unary.-", (object f, int a) => -a, doNullProp: true); t.Add("unary.-", (object f, long a) => -a, doNullProp: true); @@ -118,13 +118,13 @@ public static SymbolTable AddStandardFP(this SymbolTable t) t.Add("binary.<=", (object f, P.Any a, P.Any b) => EqualityOperators.Compare(a, b, "<="), doNullProp: true); t.Add("binary.>=", (object f, P.Any a, P.Any b) => EqualityOperators.Compare(a, b, ">="), doNullProp: true); - t.Add("single", (IEnumerable f) => f.Single(), doNullProp: true); - t.Add("skip", (IEnumerable f, long a) => f.Skip((int)a), doNullProp: true); - t.Add("first", (IEnumerable f) => f.First(), doNullProp: true); - t.Add("last", (IEnumerable f) => f.Last(), doNullProp: true); - t.Add("tail", (IEnumerable f) => f.Tail(), doNullProp: true); - t.Add("take", (IEnumerable f, long a) => f.Take((int)a), doNullProp: true); - t.Add("builtin.item", (IEnumerable f, long a) => f.Item((int)a), doNullProp: true); + t.Add("single", (IEnumerable f) => f.Single(), doNullProp: true); + t.Add("skip", (IEnumerable f, long a) => f.Skip((int)a), doNullProp: true); + t.Add("first", (IEnumerable f) => f.First(), doNullProp: true); + t.Add("last", (IEnumerable f) => f.Last(), doNullProp: true); + t.Add("tail", (IEnumerable f) => f.Tail(), doNullProp: true); + t.Add("take", (IEnumerable f, long a) => f.Take((int)a), doNullProp: true); + t.Add("builtin.item", (IEnumerable f, long a) => f.Item((int)a), doNullProp: true); t.Add("toBoolean", (P.Any f) => f.ToBoolean(), doNullProp: true); t.Add("convertsToBoolean", (P.Any f) => f.ConvertsToBoolean(), doNullProp: true); @@ -167,12 +167,12 @@ public static SymbolTable AddStandardFP(this SymbolTable t) t.Add("replace", (string f, string regex, string subst) => f.FpReplace(regex, subst), doNullProp: true); t.Add("length", (string f) => f.Length, doNullProp: true); t.Add("split", (string f, string seperator) => f.FpSplit(seperator), doNullProp: true); - t.Add("join", (IEnumerable f, string separator) => f.FpJoin(separator), doNullProp: true); - t.Add("join", (IEnumerable f) => f.FpJoin(), doNullProp: true); - t.Add("indexOf", (IEnumerable f, IScopedNode elem, int start) => f.IndexOf(elem, start), doNullProp: true); - t.Add("indexOf", (IEnumerable f, IScopedNode elem) => f.IndexOf(elem), doNullProp: true); - t.Add("lastIndexOf", (IEnumerable f, IScopedNode elem, int start) => f.LastIndexOf(elem, start), doNullProp: true); - t.Add("lastIndexOf", (IEnumerable f, IScopedNode elem) => f.LastIndexOf(elem), doNullProp: true); + t.Add("join", (IEnumerable f, string separator) => f.FpJoin(separator), doNullProp: true); + t.Add("join", (IEnumerable f) => f.FpJoin(), doNullProp: true); + t.Add("indexOf", (IEnumerable f, PocoNode elem, int start) => f.IndexOf(elem, start), doNullProp: true); + t.Add("indexOf", (IEnumerable f, PocoNode elem) => f.IndexOf(elem), doNullProp: true); + t.Add("lastIndexOf", (IEnumerable f, PocoNode elem, int start) => f.LastIndexOf(elem, start), doNullProp: true); + t.Add("lastIndexOf", (IEnumerable f, PocoNode elem) => f.LastIndexOf(elem), doNullProp: true); // Math functions t.Add("abs", (decimal f) => Math.Abs(f), doNullProp: true); @@ -189,17 +189,17 @@ public static SymbolTable AddStandardFP(this SymbolTable t) t.Add("truncate", (decimal f) => Math.Truncate((double)f), doNullProp: true); // The next two functions existed pre-normative, so we have kept them. - t.Add("is", (IScopedNode f, string name) => f.Is(name), doNullProp: true); - t.Add("as", (IEnumerable f, string name) => f.FilterType(name), doNullProp: true); + t.Add("is", (PocoNode f, string name) => f.Is(name), doNullProp: true); + t.Add("as", (IEnumerable f, string name) => f.FilterType(name), doNullProp: true); - t.Add("ofType", (IEnumerable f, string name) => f.FilterType(name), doNullProp: true); - t.Add("binary.is", (object f, IScopedNode left, string name) => left.Is(name), doNullProp: true); - t.Add("binary.as", (object f, IEnumerable left, string name) => left.FilterType(name), doNullProp: true); + t.Add("ofType", (IEnumerable f, string name) => f.FilterType(name), doNullProp: true); + t.Add("binary.is", (object f, PocoNode left, string name) => left.Is(name), doNullProp: true); + t.Add("binary.as", (object f, IEnumerable left, string name) => left.FilterType(name), doNullProp: true); // Kept for backwards compatibility, but no longer part of the spec - t.Add("binary.as", (object f, IEnumerable left, string name) => left.FilterType(name), doNullProp: true); + t.Add("binary.as", (object f, IEnumerable left, string name) => left.FilterType(name), doNullProp: true); - t.Add("extension", (IEnumerable f, string url) => f.Extension(url), doNullProp: true); + t.Add("extension", (IEnumerable f, string url) => f.Extension(url), doNullProp: true); // Logic operators do not use null propagation and may do short-cut eval t.AddLogic("binary.and", (a, b) => a.And(b)); @@ -208,18 +208,18 @@ public static SymbolTable AddStandardFP(this SymbolTable t) t.AddLogic("binary.implies", (a, b) => a.Implies(b)); // Special late-bound functions - t.Add(new CallSignature("where", typeof(IEnumerable), typeof(object), typeof(Invokee)), runWhere); - t.Add(new CallSignature("select", typeof(IEnumerable), typeof(object), typeof(Invokee)), runSelect); + t.Add(new CallSignature("where", typeof(IEnumerable), typeof(object), typeof(Invokee)), runWhere); + t.Add(new CallSignature("select", typeof(IEnumerable), typeof(object), typeof(Invokee)), runSelect); t.Add(new CallSignature("all", typeof(bool), typeof(object), typeof(Invokee)), runAll); t.Add(new CallSignature("any", typeof(bool), typeof(object), typeof(Invokee)), runAny); t.Add(new CallSignature("exists", typeof(bool), typeof(object), typeof(Invokee)), runAny); - t.Add(new CallSignature("repeat", typeof(IEnumerable), typeof(object), typeof(Invokee)), runRepeat); - t.Add(new CallSignature("trace", typeof(IEnumerable), typeof(string), typeof(object), typeof(Invokee)), Trace); - t.Add(new CallSignature("defineVariable", typeof(IEnumerable), typeof(object), typeof(string)), DefineVariable); - t.Add(new CallSignature("defineVariable", typeof(IEnumerable), typeof(object), typeof(string), typeof(Invokee)), DefineVariable); + t.Add(new CallSignature("repeat", typeof(IEnumerable), typeof(object), typeof(Invokee)), runRepeat); + t.Add(new CallSignature("trace", typeof(IEnumerable), typeof(string), typeof(object), typeof(Invokee)), Trace); + t.Add(new CallSignature("defineVariable", typeof(IEnumerable), typeof(object), typeof(string)), DefineVariable); + t.Add(new CallSignature("defineVariable", typeof(IEnumerable), typeof(object), typeof(string), typeof(Invokee)), DefineVariable); - t.Add(new CallSignature("aggregate", typeof(IEnumerable), typeof(Invokee), typeof(Invokee)), runAggregate); - t.Add(new CallSignature("aggregate", typeof(IEnumerable), typeof(Invokee), typeof(Invokee), typeof(Invokee)), runAggregate); + t.Add(new CallSignature("aggregate", typeof(IEnumerable), typeof(Invokee), typeof(Invokee)), runAggregate); + t.Add(new CallSignature("aggregate", typeof(IEnumerable), typeof(Invokee), typeof(Invokee), typeof(Invokee)), runAggregate); t.AddVar("sct", "http://snomed.info/sct"); t.AddVar("loinc", "http://loinc.org"); @@ -232,7 +232,7 @@ public static SymbolTable AddStandardFP(this SymbolTable t) } /// - /// With the regular Add extension methods, a Wrap is added to each argument to turn it into IEnumerable<IScopedNode>. + /// With the regular Add extension methods, a Wrap is added to each argument to turn it into IEnumerable<PocoNode>. /// For 'builtin.children' we know that the focus and the result are already of the correct type, /// so we created an optimized implementation avoiding the Wrap. /// @@ -240,14 +240,14 @@ public static SymbolTable AddStandardFP(this SymbolTable t) internal static void AddBuiltinChildren(this SymbolTable table) { table.Add(new CallSignature("builtin.children", - typeof(IEnumerable), - typeof(IEnumerable), + typeof(IEnumerable), + typeof(IEnumerable), typeof(string)), ( ctx, invokees) => { var iks = invokees.ToArray(); var focus = iks[0].Invoke(ctx, InvokeeFactory.EmptyArgs); - var name = (string)iks[1].Invoke(ctx, InvokeeFactory.EmptyArgs).First().Value; + var name = iks[1].Invoke(ctx, InvokeeFactory.EmptyArgs).First().GetValue() as string; var result= focus.Navigate(name); return result; @@ -264,11 +264,11 @@ private static string getCoreValueSetUrl(string id) return "http://hl7.org/fhir/ValueSet/" + id; } - private static IEnumerable runAggregate(Closure ctx, IEnumerable arguments) + private static IEnumerable runAggregate(Closure ctx, IEnumerable arguments) { var focus = arguments.First()(ctx, InvokeeFactory.EmptyArgs); var incrExpre = arguments.Skip(1).First(); - IEnumerable initialValue = []; + IEnumerable initialValue = []; if (arguments.Count() > 2) { var initialValueExpr = arguments.Skip(2).First(); @@ -278,9 +278,9 @@ private static IEnumerable runAggregate(Closure ctx, IEnumerable newFocus = [element]; + IEnumerable newFocus = [element]; var newContext = totalContext.Nest(newFocus); newContext.SetThis(newFocus); newContext.SetTotal(totalContext.GetTotal()); @@ -291,10 +291,10 @@ private static IEnumerable runAggregate(Closure ctx, IEnumerable Trace(Closure ctx, IEnumerable arguments) + private static IEnumerable Trace(Closure ctx, IEnumerable arguments) { var focus = arguments.First()(ctx, InvokeeFactory.EmptyArgs); - string name = arguments.Skip(1).First()(ctx, InvokeeFactory.EmptyArgs).FirstOrDefault()?.Value as string; + string name = arguments.Skip(1).First()(ctx, InvokeeFactory.EmptyArgs).FirstOrDefault()?.GetValue() as string; List selectArgs = [arguments.First(), .. arguments.Skip(2)]; var selectResults = runSelect(ctx, selectArgs); @@ -303,11 +303,11 @@ private static IEnumerable Trace(Closure ctx, IEnumerable return focus; } - private static IEnumerable DefineVariable(Closure ctx, IEnumerable arguments) + private static IEnumerable DefineVariable(Closure ctx, IEnumerable arguments) { Invokee[] enumerable = arguments as Invokee[] ?? arguments.ToArray(); var focus = enumerable[0](ctx, InvokeeFactory.EmptyArgs); - string name = enumerable[1](ctx, InvokeeFactory.EmptyArgs).FirstOrDefault()?.Value as string; + string name = enumerable[1](ctx, InvokeeFactory.EmptyArgs).FirstOrDefault()?.GetValue() as string; if(ctx.ResolveValue(name) is not null) throw new InvalidOperationException($"Variable {name} is already defined in this scope"); @@ -326,7 +326,7 @@ private static IEnumerable DefineVariable(Closure ctx, IEnumerable< return focus; } - private static IEnumerable runIif(Closure ctx, IEnumerable arguments) + private static IEnumerable runIif(Closure ctx, IEnumerable arguments) { // iif(criterion: expression, true-result: collection [, otherwise-result: collection]) : collection // note: short-circuit behavior is expected in this function @@ -347,23 +347,23 @@ private static IEnumerable runIif(Closure ctx, IEnumerable : otherResult == null ? [] : otherResult(newContext, InvokeeFactory.EmptyArgs); } - private static IEnumerable runWhere(Closure ctx, IEnumerable arguments) + private static IEnumerable runWhere(Closure ctx, IEnumerable arguments) { var focus = arguments.First()(ctx, InvokeeFactory.EmptyArgs); var lambda = arguments.Skip(1).First(); return CachedEnumerable.Create(runForeach()); - IEnumerable runForeach() + IEnumerable runForeach() { var index = 0; - foreach (IScopedNode element in focus) + foreach (PocoNode element in focus) { - IScopedNode[] newFocus = [element]; + PocoNode[] newFocus = [element]; var newContext = ctx.Nest(newFocus); newContext.SetThis(newFocus); - newContext.SetIndex(PocoNodeOrList.ForPrimitive(index)); + newContext.SetIndex(PocoNode.ForPrimitive(index)); index++; if (lambda(newContext, InvokeeFactory.EmptyArgs).BooleanEval() == true) @@ -372,23 +372,23 @@ IEnumerable runForeach() } } - private static IEnumerable runSelect(Closure ctx, IEnumerable arguments) + private static IEnumerable runSelect(Closure ctx, IEnumerable arguments) { var focus = arguments.First()(ctx, InvokeeFactory.EmptyArgs); var lambda = arguments.Skip(1).First(); return CachedEnumerable.Create(runForeach()); - IEnumerable runForeach() + IEnumerable runForeach() { var index = 0; - foreach (IScopedNode element in focus) + foreach (PocoNode element in focus) { - IEnumerable newFocus = [element]; + IEnumerable newFocus = [element]; var newContext = ctx.Nest(newFocus); newContext.SetThis(newFocus); - newContext.SetIndex(PocoNodeOrList.ForPrimitive(index)); + newContext.SetIndex(PocoNode.ForPrimitive(index)); index++; var result = lambda(newContext, InvokeeFactory.EmptyArgs); @@ -398,12 +398,12 @@ IEnumerable runForeach() } } - private static IEnumerable runRepeat(Closure ctx, IEnumerable arguments) + private static IEnumerable runRepeat(Closure ctx, IEnumerable arguments) { var newNodes = arguments.First()(ctx, InvokeeFactory.EmptyArgs).ToList(); var lambda = arguments.Skip(1).First(); - var fullResult = new List(); + var fullResult = new List(); while (newNodes.Any()) { @@ -411,16 +411,16 @@ private static IEnumerable runRepeat(Closure ctx, IEnumerable newFocus = [element]; + IEnumerable newFocus = [element]; var newContext = ctx.Nest(newFocus); newContext.SetThis(newFocus); - newContext.SetIndex(PocoNodeOrList.ForPrimitive(index)); + newContext.SetIndex(PocoNode.ForPrimitive(index)); index++; var candidates = lambda(newContext, InvokeeFactory.EmptyArgs); - var uniqeNewNodes = candidates.Except(fullResult, EqualityOperators.TypedElementEqualityComparer); + var uniqeNewNodes = candidates.Except(fullResult, EqualityOperators.TypedElementEqualityComparer); newNodes.AddRange(uniqeNewNodes); } @@ -431,48 +431,47 @@ private static IEnumerable runRepeat(Closure ctx, IEnumerable runAll(Closure ctx, IEnumerable arguments) + private static IEnumerable runAll(Closure ctx, IEnumerable arguments) { var focus = arguments.First()(ctx, InvokeeFactory.EmptyArgs); var lambda = arguments.Skip(1).First(); var index = 0; - foreach (IScopedNode element in focus) + foreach (PocoNode element in focus) { - IEnumerable newFocus = [element]; + IEnumerable newFocus = [element]; var newContext = ctx.Nest(newFocus); newContext.SetThis(newFocus); - newContext.SetIndex(PocoNodeOrList.ForPrimitive(index)); + newContext.SetIndex(PocoNode.ForPrimitive(index)); index++; var result = lambda(newContext, InvokeeFactory.EmptyArgs).BooleanEval(); if (result == null) return []; - if (result == false) return PocoNodeOrList.ForPrimitive(false); + if (result == false) return PocoNode.ForPrimitive(false); } - return PocoNodeOrList.ForPrimitive(true); + return PocoNode.ForPrimitive(true); } - private static IEnumerable runAny(Closure ctx, IEnumerable arguments) + private static IEnumerable runAny(Closure ctx, IEnumerable arguments) { var focus = arguments.First()(ctx, InvokeeFactory.EmptyArgs); var lambda = arguments.Skip(1).First(); var index = 0; - foreach (IScopedNode element in focus) + foreach (PocoNode element in focus) { - IEnumerable newFocus = [element]; + IEnumerable newFocus = [element]; var newContext = ctx.Nest(newFocus); newContext.SetThis(newFocus); - newContext.SetIndex(PocoNodeOrList.ForPrimitive(index)); + newContext.SetIndex(PocoNode.ForPrimitive(index)); index++; var result = lambda(newContext, InvokeeFactory.EmptyArgs).BooleanEval(); - if (result == true) return PocoNodeOrList.ForPrimitive(true); + if (result == true) return PocoNode.ForPrimitive(true); } - - //return PocoNodeOrList.ForPrimitive(false); - return PocoNodeOrList.Root(new FhirBoolean(false)); + + return PocoNode.Root(new FhirBoolean(false)); } } } \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/FhirPath/Expressions/Typecasts.cs b/src/Hl7.Fhir.Base/FhirPath/Expressions/Typecasts.cs index 76c4955004..24e1f2ad11 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Expressions/Typecasts.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Expressions/Typecasts.cs @@ -11,7 +11,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; using P = Hl7.Fhir.ElementModel.Types; namespace Hl7.FhirPath.Expressions @@ -25,34 +24,30 @@ internal static class Typecasts private static Cast makeNativeCast(Type to) => source => Convert.ChangeType(source, to); - private static IScopedNode any2primitiveTypedElement(object source) => ElementNode.ForPrimitive(source).ToScopedNode(); + private static PocoNode any2primitiveTypedElement(object source) => PocoNode.ForAnyPrimitive(source); - private static IEnumerable any2List(object source) => ElementNode.CreateList(source).ToScopedNodes(); + private static IEnumerable any2SingleItemList(object source) => PocoNode.ForAnyPrimitive(source); private static P.Quantity tryQuantity(object source) { - if (source is IScopedNode element) + if (source is PocoNode element) { - if (element.Type.HasFlag(NodeType.Quantity)) + if (element is {Poco: Quantity}) { // Need to downcast from a FHIR Quantity to a System.Quantity return ParseQuantity(element); } else - throw new InvalidCastException($"Cannot convert from '{element.InstanceType}' to Quantity"); + throw new InvalidCastException($"Cannot convert from '{element.Poco.TypeName}' to Quantity"); } throw new InvalidCastException($"Cannot convert from '{source.GetType().Name}' to Quantity"); } - internal static P.Quantity ParseQuantity(IScopedNode qe) + internal static P.Quantity ParseQuantity(PocoNode qe) { - var value = qe.Children("value").SingleOrDefault()?.Value as decimal?; - if (value == null) return null; - - var unit = qe.Children("code").SingleOrDefault()?.Value as string; - return new P.Quantity(value.Value, unit); + return (qe.Poco as Quantity)?.ToSystemQuantity(); } private static Cast getImplicitCast(object f, Type to) @@ -62,11 +57,11 @@ private static Cast getImplicitCast(object f, Type to) if (to == typeof(object)) return id; if (from.CanBeTreatedAsType(to)) return id; - // this check seems weird, but PocoElementNode both implements IScopedNode and IEnumerable for the sake of backwards compatibility - bool fromElemList = from.CanBeTreatedAsType(typeof(IEnumerable)) && !from.CanBeTreatedAsType(typeof(IScopedNode)); - if (to == typeof(P.Quantity) && from.CanBeTreatedAsType(typeof(IScopedNode))) return tryQuantity; - if (to == typeof(IScopedNode) && (!fromElemList)) return any2primitiveTypedElement; - if (to == typeof(IEnumerable)) return any2List; + // this check seems weird, but PocoElementNode both implements PocoNode and IEnumerable for the sake of backwards compatibility + bool fromElemList = from.CanBeTreatedAsType(typeof(IEnumerable)) && !from.CanBeTreatedAsType(typeof(PocoNode)); + if (to.CanBeTreatedAsType(typeof(P.Any)) && from.CanBeTreatedAsType(typeof(PocoNode))) return tryQuantity; + if (to == typeof(PocoNode) && !fromElemList) return any2primitiveTypedElement; + if (to == typeof(IEnumerable)) return any2SingleItemList; if (from == typeof(long) && (to == typeof(decimal) || to == typeof(decimal?))) return makeNativeCast(typeof(decimal)); if (from == typeof(long?) && to == typeof(decimal?)) return makeNativeCast(typeof(decimal?)); @@ -80,7 +75,8 @@ private static Cast getImplicitCast(object f, Type to) if (typeof(P.Any).IsAssignableFrom(to) && !fromElemList) { - if (f is IScopedNode te && te.Type.HasFlag(NodeType.Quantity)) return o => ParseQuantity((IScopedNode)o); + if (f is PocoNode {Poco: Quantity} q) + return _ => q; return o => P.Any.Convert(o); } @@ -104,38 +100,33 @@ private static Cast getImplicitCast(object f, Type to) /// The level to unbox to. /// /// The level of unboxing is specified using a type. The highest level - /// being an followed by - /// followed by a primitive runtime type. + /// being an followed by + /// followed by a primitive runtime type. /// internal static object UnboxTo(object instance, Type to) { if (instance == null) return null; - if (instance is IEnumerable list) + if (instance is IEnumerable list) { var cachedEnum = CachedEnumerable.Create(list); - if (to.CanBeTreatedAsType(typeof(IEnumerable))) return cachedEnum; + if (!to.CanBeTreatedAsType(typeof(PocoNode)) && to.CanBeTreatedAsType(typeof(IEnumerable))) return cachedEnum; + if (!cachedEnum.Any()) return null; if (cachedEnum.Count() == 1) instance = cachedEnum.Single(); } - if (instance is IScopedNode element) + if (instance is PocoNode element) { - if (to.CanBeTreatedAsType(typeof(IScopedNode))) return instance; + if (to.CanBeTreatedAsType(typeof(PocoNode))) return instance; if (to == typeof(object)) return instance; - // HACK - We assume the primitives - // start with a lower-case letter, which is true in FHIR but not - // in general. When this is a System.* type, we know this is supposed - // to represent the object in Value. - - var isPrimitive = element.Value != null || - (element.InstanceType != null && - Char.IsLower(element.InstanceType[0]) || element.InstanceType.StartsWith("System.")); - if (isPrimitive) - instance = element.Value; + if (element is PrimitiveNode { Value: { } value }) + { + instance = value; + } } return instance; @@ -193,8 +184,8 @@ public static object CastTo(object source, Type to) } //if source == null, or unboxed source == null.... - if (to == typeof(IEnumerable)) - return Array.Empty(); + if (to == typeof(IEnumerable)) + return Array.Empty(); if (to.IsNullable()) return null; else @@ -212,10 +203,10 @@ public static bool IsNullable(this Type t) public static string ReadableFhirPathName(object value) { - if (value is IScopedNode te) - return te.InstanceType; + if (value is PocoNode te) + return te.Poco.TypeName; - if (value is IEnumerable ete) + if (value is IEnumerable ete) { var values = ete.ToList(); var types = ete.Select(te => ReadableFhirPathName(te)).Distinct(); @@ -228,9 +219,9 @@ public static string ReadableFhirPathName(object value) public static string ReadableTypeName(Type t) { - if (t.CanBeTreatedAsType(typeof(IEnumerable))) + if (t.CanBeTreatedAsType(typeof(IEnumerable))) return "collection"; - else if (t.CanBeTreatedAsType(typeof(IScopedNode))) + else if (t.CanBeTreatedAsType(typeof(PocoNode))) return "any type"; else if (t.CanBeTreatedAsType(typeof(P.Any))) return "FhirPath type " + t.Name; diff --git a/src/Hl7.Fhir.Base/FhirPath/FPSystemTypes.cs b/src/Hl7.Fhir.Base/FhirPath/FPSystemTypes.cs new file mode 100644 index 0000000000..a13710cae2 --- /dev/null +++ b/src/Hl7.Fhir.Base/FhirPath/FPSystemTypes.cs @@ -0,0 +1,87 @@ +using Hl7.Fhir.Model; +using P = Hl7.Fhir.ElementModel.Types; + +namespace Hl7.FhirPath; + +internal abstract class CqlPrimitive : DynamicPrimitive; + +internal class FPTime : CqlPrimitive +{ + public FPTime(P.Time value) + { + DynamicTypeName = "System.Time"; + ObjectValue = value; + } +} + +internal class FPDateTime : CqlPrimitive +{ + public FPDateTime(P.DateTime value) + { + DynamicTypeName = "System.DateTime"; + ObjectValue = value; + } +} + +internal class FPDate : CqlPrimitive +{ + public FPDate(P.Date value) + { + DynamicTypeName = "System.Date"; + ObjectValue = value; + } +} + +internal class FPBoolean : CqlPrimitive +{ + public FPBoolean(bool value) + { + DynamicTypeName = "System.Boolean"; + ObjectValue = value; + } +} + +internal class FPInteger : CqlPrimitive +{ + public FPInteger(int value) + { + DynamicTypeName = "System.Integer"; + ObjectValue = value; + } +} + +internal class FPLong : CqlPrimitive +{ + public FPLong(long value) + { + DynamicTypeName = "System.Long"; + ObjectValue = value; + } +} + +internal class FPDecimal : CqlPrimitive +{ + public FPDecimal(decimal value) + { + DynamicTypeName = "System.Decimal"; + ObjectValue = value; + } +} + +internal class FPString : CqlPrimitive +{ + public FPString(string value) + { + DynamicTypeName = "System.String"; + ObjectValue = value; + } +} + +internal class FPQuantity : CqlPrimitive +{ + public FPQuantity(P.Quantity value) + { + DynamicTypeName = "System.Quantity"; + ObjectValue = value; + } +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/FhirPath/FhirEvaluationContext.cs b/src/Hl7.Fhir.Base/FhirPath/FhirEvaluationContext.cs index fe0333323f..e84fefdc98 100644 --- a/src/Hl7.Fhir.Base/FhirPath/FhirEvaluationContext.cs +++ b/src/Hl7.Fhir.Base/FhirPath/FhirEvaluationContext.cs @@ -26,21 +26,9 @@ public FhirEvaluationContext() public ICodeValidationTerminologyService? TerminologyService { get; set; } - private static IScopedNode toNearestResource(ScopedNode node) - { - var scan = node; - - while (scan.AtResource == false && scan.ParentResource is not null) - { - scan = scan.ParentResource; - } - - return scan; - } - - private Func? _elementResolver; + private Func? _elementResolver; - public Func? ElementResolver + public Func? ElementResolver { get { return _elementResolver; } set { _elementResolver = value; } diff --git a/src/Hl7.Fhir.Base/FhirPath/FhirPathCompilerCache.cs b/src/Hl7.Fhir.Base/FhirPath/FhirPathCompilerCache.cs index 585ea53cf1..a66b63d97c 100644 --- a/src/Hl7.Fhir.Base/FhirPath/FhirPathCompilerCache.cs +++ b/src/Hl7.Fhir.Base/FhirPath/FhirPathCompilerCache.cs @@ -59,7 +59,7 @@ public void Clear() /// Expression which is to be evaluated /// Context of the evaluation /// The result(s) of the expression - public IEnumerable Select(IScopedNode input, string expression, EvaluationContext? ctx = null) + public IEnumerable Select(PocoNode input, string expression, EvaluationContext? ctx = null) { var evaluator = GetCompiledExpression(expression); return evaluator(input, ctx ?? new EvaluationContext()); @@ -72,7 +72,7 @@ public IEnumerable Select(IScopedNode input, string expression, Eva /// Expression which is to be evaluated /// Context of the evaluation /// The single result of the expression, and null if the expression returns multiple results - public object? Scalar(IScopedNode input, string expression, EvaluationContext? ctx = null) + public object? Scalar(PocoNode input, string expression, EvaluationContext? ctx = null) { var evaluator = GetCompiledExpression(expression); return evaluator.Scalar(input, ctx ?? new EvaluationContext()); @@ -85,7 +85,7 @@ public IEnumerable Select(IScopedNode input, string expression, Eva /// Expression which is to be evaluated /// Context of the evaluation /// True if expression returns true of empty, otheriwse false - public bool Predicate(IScopedNode input, string expression, EvaluationContext? ctx = null) + public bool Predicate(PocoNode input, string expression, EvaluationContext? ctx = null) { var evaluator = GetCompiledExpression(expression); return evaluator.Predicate(input, ctx ?? new EvaluationContext()); @@ -98,7 +98,7 @@ public bool Predicate(IScopedNode input, string expression, EvaluationContext? c /// Expression which is to be evaluated /// Context of the evaluation /// True if expression returns true , and false if expression returns empty of false. - public bool IsTrue(IScopedNode input, string expression, EvaluationContext? ctx = null) + public bool IsTrue(PocoNode input, string expression, EvaluationContext? ctx = null) { var evaluator = GetCompiledExpression(expression); return evaluator.IsTrue(input, ctx ?? new EvaluationContext()); @@ -113,7 +113,7 @@ public bool IsTrue(IScopedNode input, string expression, EvaluationContext? ctx /// Expression which is to be evaluated /// Context of the evaluation /// True if the result of an expression is equal to a given boolean, otherwise false - public bool IsBoolean(IScopedNode input, string expression, bool value, EvaluationContext? ctx = null) + public bool IsBoolean(PocoNode input, string expression, bool value, EvaluationContext? ctx = null) { var evaluator = GetCompiledExpression(expression); return evaluator.IsBoolean(value, input, ctx ?? new EvaluationContext()); diff --git a/src/Hl7.Fhir.Base/FhirPath/Functions/CollectionOperators.cs b/src/Hl7.Fhir.Base/FhirPath/Functions/CollectionOperators.cs index 8a9cfdabb8..3e67c8d1a1 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Functions/CollectionOperators.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Functions/CollectionOperators.cs @@ -9,6 +9,7 @@ using Hl7.Fhir.ElementModel; using Hl7.Fhir.Model; using Hl7.Fhir.Utility; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -17,59 +18,59 @@ namespace Hl7.FhirPath.Functions { internal static class CollectionOperators { - public static bool? BooleanEval(this IEnumerable focus) + public static bool? BooleanEval(this IEnumerable focus) { - if (!focus.Any()) return null; + PocoNode[] enumerable = focus.ToArray(); + if (!enumerable.Any()) return null; - if (focus.Count() == 1 && focus.Single().Value is bool boolean) + if (enumerable.Length == 1 && enumerable.Single() is PrimitiveNode {Value: bool boolean}) { return boolean; } // Otherwise, we have "some" content, which we'll consider "true" - else - return true; + return true; } - public static bool Not(this IEnumerable focus) + public static bool Not(this IEnumerable focus) => focus.Count() > 1 ? throw Error.InvalidOperation($"Operator {nameof(Not)} is not applicable for collections with more than one item.") : !focus.BooleanEval().Value; - public static IEnumerable DistinctUnion(this IEnumerable a, IEnumerable b) - => a.Union(b, EqualityOperators.TypedElementEqualityComparer); + public static IEnumerable DistinctUnion(this IEnumerable a, IEnumerable b) + => a.Union(b, EqualityOperators.TypedElementEqualityComparer); - public static IEnumerable Item(this IEnumerable focus, int index) + public static IEnumerable Item(this IEnumerable focus, int index) => focus.Skip(index).Take(1); - public static IScopedNode Last(this IEnumerable focus) + public static PocoNode Last(this IEnumerable focus) => focus.Reverse().First(); - public static IEnumerable Tail(this IEnumerable focus) + public static IEnumerable Tail(this IEnumerable focus) => focus.Skip(1); - public static bool Contains(this IEnumerable focus, IScopedNode value) + public static bool Contains(this IEnumerable focus, PocoNode value) => focus.Contains(value, EqualityOperators.TypedElementEqualityComparer); - public static IEnumerable Distinct(this IEnumerable focus) - => focus.Distinct(EqualityOperators.TypedElementEqualityComparer); + public static IEnumerable Distinct(this IEnumerable focus) + => focus.Distinct(EqualityOperators.TypedElementEqualityComparer); - public static bool IsDistinct(this IEnumerable focus) + public static bool IsDistinct(this IEnumerable focus) => focus.Distinct(EqualityOperators.TypedElementEqualityComparer).Count() == focus.Count(); - public static bool SubsetOf(this IEnumerable focus, IEnumerable other) + public static bool SubsetOf(this IEnumerable focus, IEnumerable other) => focus.All(fitem => other.Contains(fitem)); - public static IEnumerable Intersect(this IEnumerable focus, IEnumerable other) - => focus.Intersect(other, EqualityOperators.TypedElementEqualityComparer); + public static IEnumerable Intersect(this IEnumerable focus, IEnumerable other) + => focus.Intersect(other, EqualityOperators.TypedElementEqualityComparer); - public static IEnumerable Exclude(this IEnumerable focus, IEnumerable other) + public static IEnumerable Exclude(this IEnumerable focus, IEnumerable other) => focus.Where(f => !other.Contains(f)); - public static int IndexOf(this IEnumerable focus, IScopedNode item, int start = 0) + public static int IndexOf(this IEnumerable focus, PocoNode item, int start = 0) { - var typedElements = focus as IScopedNode[] ?? focus.ToArray(); + var typedElements = focus as PocoNode[] ?? focus.ToArray(); for (int i = start; i < typedElements.Length; i++) { if (EqualityOperators.TypedElementEqualityComparer.Equals(typedElements[i], item)) @@ -80,9 +81,9 @@ public static int IndexOf(this IEnumerable focus, IScopedNode item, return -1; } - public static int LastIndexOf(this IEnumerable focus, IScopedNode item, int to = -1) + public static int LastIndexOf(this IEnumerable focus, PocoNode item, int to = -1) { - var typedElements = focus as IScopedNode[] ?? focus.ToArray(); + var typedElements = focus as PocoNode[] ?? focus.ToArray(); to = to < 0 ? typedElements.Count() - 1 : to; for (int i = to; i >= 0; i--) { @@ -95,36 +96,35 @@ public static int LastIndexOf(this IEnumerable focus, IScopedNode i } - public static IEnumerable Navigate(this IEnumerable elements, string name) + public static IEnumerable Navigate(this IEnumerable elements, string name) => elements.SelectMany(e => e.Navigate(name)); - public static IEnumerable Navigate(this IScopedNode element, string name) + public static IEnumerable Navigate(this PocoNode element, string name) { if (char.IsUpper(name[0])) { // If we are at a resource, we should match a path that is possibly not rooted in the resource // (e.g. doing "name.family" on a Patient is equivalent to "Patient.name.family") - var baseClasses = new[] { "Resource", "DomainResource" }; - if (element.InstanceType == name || baseClasses.Contains(name)) + if(element.Is(name)) { - return new List() { element }; + return new List() { element }; } } - return element.Children(name); + return element.Child(name) ?? Enumerable.Empty(); } - public static string FpJoin(this IEnumerable collection, string separator = null) + public static string FpJoin(this IEnumerable collection, string separator = null) { //if the collection is empty return the empty result if (!collection.Any()) return string.Empty; //only join collections with string values inside - if (!collection.All(c => c.Value is string)) + if (!collection.All(c => c.GetValue() is string)) throw Error.InvalidOperation("Join function can only be performed on string collections."); - var values = collection.Select(n => n.Value); + var values = collection.Select(n => n.GetValue()); return string.Join(separator, values); } } diff --git a/src/Hl7.Fhir.Base/FhirPath/Functions/EqualityOperators.cs b/src/Hl7.Fhir.Base/FhirPath/Functions/EqualityOperators.cs index edcc323763..839761a8ba 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Functions/EqualityOperators.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Functions/EqualityOperators.cs @@ -9,6 +9,7 @@ #nullable enable using Hl7.Fhir.ElementModel; +using Hl7.Fhir.Introspection; using Hl7.Fhir.Model; using Hl7.FhirPath; using Hl7.FhirPath.Expressions; @@ -21,7 +22,7 @@ namespace Hl7.FhirPath.Functions { public static class EqualityOperators { - public static bool? IsEqualTo(this IEnumerable left, IEnumerable right, bool compareNames = false) + public static bool? IsEqualTo(this IEnumerable left, IEnumerable right, bool compareNames = false) { // If one or both of the arguments is an empty collection, a comparison operator will return an empty collection. // (though we might handle this more generally with the null-propagating functionality of the compiler @@ -51,7 +52,7 @@ public static class EqualityOperators // comparison of the elements, while this one does a comparison on equivalence rules from the FhirPath spec, // which differ considerably (e.g. names are not compared, FHIR Quantity is compared to System.Quantity, // there are specified coercions, etc.) - public static bool? IsEqualTo(this IScopedNode left, IScopedNode right, bool compareNames = false) + public static bool? IsEqualTo(this ITypedElement left, ITypedElement right, bool compareNames = false) { // If one or both of the arguments is an empty collection, a comparison operator will return an empty collection. // (though we might handle this more generally with the null-propagating functionality of the compiler @@ -67,10 +68,14 @@ public static class EqualityOperators // TODO: this is actually a cast with knowledge of FHIR->System mappings, we don't want that here anymore // Convert quantities - if (left.Type.HasFlag(NodeType.Quantity) && l == null) - l = Typecasts.ParseQuantity(left); - if (right.Type.HasFlag(NodeType.Quantity) && r == null) - r = Typecasts.ParseQuantity(right); + if (left.InstanceType == "Quantity" && l == null) + l = left is PocoNode node + ? Typecasts.ParseQuantity(node) + : Typecasts.ParseQuantity(left.ToPoco(ModelInspector.ForAssembly(typeof(Quantity).Assembly)).ToPocoNode()); + if (right.InstanceType == "Quantity" && r == null) + r = right is PocoNode node + ? Typecasts.ParseQuantity(node) + : Typecasts.ParseQuantity(right.ToPoco(ModelInspector.ForAssembly(typeof(Quantity).Assembly)).ToPocoNode()); // Compare primitives (or extended primitives) if (l != null && r != null && P.Any.TryConvert(l, out var lAny) && P.Any.TryConvert(r, out var rAny)) @@ -129,7 +134,7 @@ static P.Any upcastOne(P.Any value, P.Any other) => }; } - public static bool IsEquivalentTo(this IEnumerable left, IEnumerable right, bool compareNames = false) + public static bool IsEquivalentTo(this IEnumerable left, IEnumerable right, bool compareNames = false) { var r = right.ToList(); int count = 0; @@ -144,7 +149,7 @@ public static bool IsEquivalentTo(this IEnumerable left, IEnumerabl } - public static bool IsEquivalentTo(this IScopedNode left, IScopedNode right, bool compareNames = false) + public static bool IsEquivalentTo(this ITypedElement left, ITypedElement right, bool compareNames = false) { // Note that because of this behaviour, we should switch off null-propagating behaviour of IsEquivalent to if (left is null && right is null) return true; @@ -157,10 +162,14 @@ public static bool IsEquivalentTo(this IScopedNode left, IScopedNode right, bool // TODO: this is actually a cast with knowledge of FHIR->System mappings, we don't want that here anymore // Convert quantities - if (left.Type.HasFlag(NodeType.Quantity) && l == null) - l = Typecasts.ParseQuantity(left); - if (right.Type.HasFlag(NodeType.Quantity) && r == null) - r = Typecasts.ParseQuantity(right); + if (left.InstanceType == "Quantity" && l == null) + l = left is PocoNode node + ? Typecasts.ParseQuantity(node) + : Typecasts.ParseQuantity(left.ToPoco(ModelInspector.ForAssembly(typeof(Quantity).Assembly)).ToPocoNode()); + if (right.InstanceType == "Quantity" && r == null) + r = right is PocoNode node + ? Typecasts.ParseQuantity(node) + : Typecasts.ParseQuantity(right.ToPoco(ModelInspector.ForAssembly(typeof(Quantity).Assembly)).ToPocoNode()); // Compare primitives (or extended primitives) if (l != null && r != null && P.Any.TryConvert(l, out var lAny) && P.Any.TryConvert(r, out var rAny)) @@ -181,7 +190,7 @@ public static bool IsEquivalentTo(this IScopedNode left, IScopedNode right, bool return false; } - static bool namesAreEquivalent(IScopedNode le, IScopedNode ri) + static bool namesAreEquivalent(ITypedElement le, ITypedElement ri) { if (le.Name == "id" && ri.Name == "id") return true; // IN FHIR: don't compare 'id' elements for equivalence if (le.Name != ri.Name) return false; @@ -243,11 +252,11 @@ public static bool IsEquivalentTo(P.Any? left, P.Any? right) } } - public static readonly IEqualityComparer TypedElementEqualityComparer = new ValueProviderEqualityComparer(); + public static readonly IEqualityComparer TypedElementEqualityComparer = new ValueProviderEqualityComparer(); - private class ValueProviderEqualityComparer : IEqualityComparer + private class ValueProviderEqualityComparer : IEqualityComparer { - public bool Equals(IScopedNode? x, IScopedNode? y) + public bool Equals(ITypedElement? x, ITypedElement? y) { if (x is null && y is null) return true; if (x is null || y is null) return false; @@ -259,11 +268,11 @@ public bool Equals(IScopedNode? x, IScopedNode? y) return x.IsEqualTo(y) == true; } - public int GetHashCode(IScopedNode element) + public int GetHashCode(ITypedElement element) { var result = element.Value != null ? element.Value.GetHashCode() : 0; - if (element is IScopedNode element1) + if (element is ITypedElement element1) { var childnames = string.Concat(element1.Children().Select(c => c.Name)); if (!string.IsNullOrEmpty(childnames)) diff --git a/src/Hl7.Fhir.Base/FhirPath/Functions/StringOperators.cs b/src/Hl7.Fhir.Base/FhirPath/Functions/StringOperators.cs index 2353b93785..2e7d3f995e 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Functions/StringOperators.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Functions/StringOperators.cs @@ -34,13 +34,13 @@ public static string FpSubstring(this string me, long start, long? length) return me.Substring((int)start, (int)l); } - public static IScopedNode FpIndexOf(this string me, string fragment) + public static PocoNode FpIndexOf(this string me, string fragment) { - return PocoNodeOrList.ForPrimitive(me.IndexOf(fragment, StringComparison.Ordinal)); + return PocoNode.ForPrimitive(me.IndexOf(fragment, StringComparison.Ordinal)); } - public static IEnumerable ToChars(this string me) => - PocoNodeOrList.FromList(me.Select(c => c.ToString())); + public static IEnumerable ToChars(this string me) => + PocoNode.FromList(me.Select(c => c.ToString())); public static string FpReplace(this string me, string find, string replace) { @@ -54,10 +54,10 @@ public static string FpReplace(this string me, string find, string replace) return me.Replace(find, replace); } - public static IEnumerable FpSplit(this string me, string seperator) + public static IEnumerable FpSplit(this string me, string seperator) { var results = me.Split(new[] { seperator }, StringSplitOptions.None).ToArray(); - return PocoNodeOrList.FromList(results); + return PocoNode.FromList(results); } public static string FpEncode(this string me, string encoding) diff --git a/src/Hl7.Fhir.Base/FhirPath/Functions/TypeOperators.cs b/src/Hl7.Fhir.Base/FhirPath/Functions/TypeOperators.cs index 8d8a3a20b1..95fa1444ab 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Functions/TypeOperators.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Functions/TypeOperators.cs @@ -10,28 +10,34 @@ using System.Collections.Generic; using System.Linq; using Hl7.Fhir.ElementModel; +using Hl7.Fhir.Introspection; using Hl7.Fhir.Model; using Hl7.Fhir.Utility; using System.Runtime.CompilerServices; +#nullable enable + namespace Hl7.FhirPath.Functions { internal static class TypeOperators { - public static bool Is(this IScopedNode focus, string type) + public static bool Is(this PocoNode focus, string type) { - if (focus.InstanceType != null) + var selfAndBaseClasses = getBaseClasses(focus.Poco.GetType()) + .Select(t => ModelInspector.ForType(t).GetFhirTypeNameForType(t)) + .Append(((ITypedElement)focus).InstanceType); + return selfAndBaseClasses.Any(typeString => Is(typeString, type)); + + static IEnumerable getBaseClasses(Type t) { - return Is(focus.InstanceType, type); // I have no information about classes/subclasses + return t.BaseType == null ? [] : getBaseClasses(t.BaseType).Append(t); } - else - throw Error.InvalidOperation("Is operator is called on untyped data"); } - public static bool Is(string instanceType, string declaredType) + public static bool Is(string? instanceType, string declaredType) { // Bit of a hack, this hardwires the FhirPath implementation to FHIR - if (!instanceType.Contains(".")) instanceType = "FHIR." + instanceType; + if (instanceType?.Contains(".") is false) instanceType = "FHIR." + instanceType; if (declaredType.Contains(".")) return instanceType == declaredType; else @@ -41,10 +47,7 @@ public static bool Is(string instanceType, string declaredType) } } - public static IEnumerable FilterType(this IEnumerable focus, string typeName) + public static IEnumerable FilterType(this IEnumerable focus, string typeName) => focus.Where(item => item.Is(typeName)); - - public static IScopedNode CastAs(this IScopedNode focus, string typeName) - => focus.Is(typeName) ? focus : null; } } diff --git a/src/Hl7.Fhir.Base/FhirPath/Functions/UtilityOperators.cs b/src/Hl7.Fhir.Base/FhirPath/Functions/UtilityOperators.cs index 94ef726a38..f955497594 100644 --- a/src/Hl7.Fhir.Base/FhirPath/Functions/UtilityOperators.cs +++ b/src/Hl7.Fhir.Base/FhirPath/Functions/UtilityOperators.cs @@ -9,13 +9,13 @@ namespace Hl7.FhirPath.Functions { internal static class UtilityOperators { - public static IEnumerable Extension(this IEnumerable focus, string url) + public static IEnumerable Extension(this IEnumerable focus, string url) { return focus.Navigate("extension") - .Where(es => es.Children("url").SingleOrDefault().Value as string == url); + .Where(es => es.Child("url")?.Value as string == url); } - public static IEnumerable Trace(this IEnumerable focus, string name, EvaluationContext ctx) + public static IEnumerable Trace(this IEnumerable focus, string name, EvaluationContext ctx) { ctx.Tracer?.Invoke(name, focus); return focus; diff --git a/src/Hl7.Fhir.Base/FhirPath/TypedElementFPExtensions.cs b/src/Hl7.Fhir.Base/FhirPath/TypedElementFPExtensions.cs index a394069398..b535f0bec8 100644 --- a/src/Hl7.Fhir.Base/FhirPath/TypedElementFPExtensions.cs +++ b/src/Hl7.Fhir.Base/FhirPath/TypedElementFPExtensions.cs @@ -22,25 +22,25 @@ public static class IValueProviderFPExtensions private static Lazy CACHE = new(() => new(compiler: null, cacheSize: MAX_FP_EXPRESSION_CACHE_SIZE)); - /// + /// public static IEnumerable Select(this ITypedElement input, string expression, EvaluationContext? ctx = null) - => CACHE.Value.Select(input.ToScopedNode(), expression, ctx); + => CACHE.Value.Select(input.ToPocoNode(), expression, ctx); - /// + /// public static object? Scalar(this ITypedElement input, string expression, EvaluationContext? ctx = null) - => CACHE.Value.Scalar(input.ToScopedNode(), expression, ctx); + => CACHE.Value.Scalar(input.ToPocoNode(), expression, ctx); - /// + /// public static bool Predicate(this ITypedElement input, string expression, EvaluationContext? ctx = null) - => CACHE.Value.Predicate(input.ToScopedNode(), expression, ctx); + => CACHE.Value.Predicate(input.ToPocoNode(), expression, ctx); - /// + /// public static bool IsTrue(this ITypedElement input, string expression, EvaluationContext? ctx = null) - => CACHE.Value.IsTrue(input.ToScopedNode(), expression, ctx); + => CACHE.Value.IsTrue(input.ToPocoNode(), expression, ctx); - /// + /// public static bool IsBoolean(this ITypedElement input, string expression, bool value, EvaluationContext? ctx = null) - => CACHE.Value.IsBoolean(input.ToScopedNode(), expression, value, ctx); + => CACHE.Value.IsBoolean(input.ToPocoNode(), expression, value, ctx); /// /// Reinitialize the cache. This method is only meant for the unit tests, but can be made public later. We need some refactoring here, I (MV) think. diff --git a/src/Hl7.Fhir.Base/Model/Base.Extensions.Conversions.cs b/src/Hl7.Fhir.Base/Model/Base.Extensions.Conversions.cs new file mode 100644 index 0000000000..609694efe7 --- /dev/null +++ b/src/Hl7.Fhir.Base/Model/Base.Extensions.Conversions.cs @@ -0,0 +1,60 @@ +using Hl7.Fhir.ElementModel; +using Hl7.Fhir.Introspection; +using Hl7.Fhir.Serialization; +using Hl7.Fhir.Utility; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Hl7.Fhir.Model; + +#nullable enable + +public static partial class BaseExtensions +{ + /// + /// Converts a Poco to an ITypedElement. + /// + /// The Poco that should be converted to an . + /// The containing the POCO classes to be used for deserialization. + /// + /// + public static ITypedElement ToTypedElementLegacy(this Base @base, ModelInspector modelInspector, string? rootName = null) + => new PocoElementNode(modelInspector, @base, rootName: rootName); + + + /// + /// Creates an adapter which implements ITypedElement on top of a POCO instance, with explicit version-specific metadata. + /// + /// The POCO instance + /// The ModelInspector instance supplying version-specific metadata for the instance + /// The name you wish to have at the root of the tree. This will determine e.g. the root element name for serialization. + /// If none is given, the type of the underlying poco will be used. + /// The implementation of this method has changed. If you notice regressions, please let the SDK team know. + /// In the meantime, you can restore the old behaviour with a call to +#if NETSTANDARD2_1 + [Obsolete("The implementation of this method has changed to use our new model stack. If you want to try the new behaviour, "+ + "either ignore this warning or call ToPocoNode(). For reverting to the old behaviour, call .ToTypedElementLegacy()")] +#else + [Experimental("SDK0001")] +#endif + public static ITypedElement ToTypedElement(this Base @base, ModelInspector inspector, string? rootName = null) => + @base.ToPocoNode(inspector, rootName); + + /// + /// Converts a Poco to a PocoNode. + /// + /// The Poco that should be converted to an . + /// An optional that should be used to access metadata about the resource. + /// An optional nome for the node at the root of the tree. + public static PocoNode ToPocoNode(this Base @base, ModelInspector? inspector = null, string? rootName = null) + { + var result = PocoNodeOrList.Root(@base, rootName); + if(inspector is not null) + ((IAnnotatable)result).AddAnnotation(inspector); + + return result; + } + + public static ISourceNode ToSourceNode(this Base @base, ModelInspector inspector, string? rootName = null) => + @base.ToPocoNode(inspector, rootName); +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Model/Base.Extensions.cs b/src/Hl7.Fhir.Base/Model/Base.Extensions.cs index 82f735e5e8..f9259af2cf 100644 --- a/src/Hl7.Fhir.Base/Model/Base.Extensions.cs +++ b/src/Hl7.Fhir.Base/Model/Base.Extensions.cs @@ -10,7 +10,7 @@ namespace Hl7.Fhir.Model; -public static class BaseExtensions +public static partial class BaseExtensions { [Obsolete("Use EnumerateElements() instead. Note that with EnumerateElements(), the elements 'div' and 'id' are not FhirStrings, but XHtml and FhirUri respectively.")] public static IEnumerable Children(this Base instance) diff --git a/src/Hl7.Fhir.Base/Model/Base.TypedElement.cs b/src/Hl7.Fhir.Base/Model/Base.TypedElement.cs deleted file mode 100644 index 3da2bccfd9..0000000000 --- a/src/Hl7.Fhir.Base/Model/Base.TypedElement.cs +++ /dev/null @@ -1,188 +0,0 @@ -#if true - -#nullable enable - -using Hl7.Fhir.ElementModel; -using Hl7.Fhir.Serialization; -using Hl7.Fhir.Specification; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Runtime.CompilerServices; -using P = Hl7.Fhir.ElementModel.Types; - -namespace Hl7.Fhir.Model; - -internal record ScopeInformation(IScopedNode? Parent, string Name, int? Index); - -public abstract partial class Base -{ - - // #region ScopeInformation - // - // [NonSerialized] private ScopeInformation? _scopeInfo; - // - // private ScopeInformation ScopeInfo - // { - // get => LazyInitializer.EnsureInitialized(ref _scopeInfo, () => BuildRoot())!; - // set => _scopeInfo = value; - // } - // - // internal ScopeInformation BuildRoot(string? rootName = null) => new(null, rootName ?? TypeName, null); - // - // internal Base WithScopeInfo(ScopeInformation info) - // { - // this.ScopeInfo = info; - // return this; - // } - // - // #endregion - // - //#region ITypedElement - // - // IEnumerable ITypedElement.Children(string? name) => - // this.GetElementPairs() - // .Where(ep => (name == null || name == ep.Key)) - // .SelectMany, Base>(ep => - // (ep.Key, ep.Value) switch - // { - // (_, Base b) => (IEnumerable) [b.WithScopeInfo(new ScopeInformation(this, ep.Key, null))], - // (_, IEnumerable list) => list.Select((item, idx) => - // item.WithScopeInfo(new ScopeInformation(this, ep.Key, idx))), - // ("url", string s) when this is Extension => - // [new FhirUri(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))], - // ("id", string s) when this is Element => - // [new FhirString(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))], - // ("value", _) => [], - // _ => throw new InvalidOperationException("Unexpected system primitive in child list") - // } - // ); - // - // string ITypedElement.Name => ScopeInfo.Name; - // - // string ITypedElement.InstanceType - // { - // get - // { - // if (this is BackboneElement) - // return "BackboneElement"; - // - // if (this is Element && TypeName.Contains('.')) - // return "Element"; - // - // return ScopeInfo switch - // { - // { Parent: Extension, Name: "url" } => "uri", - // { Parent: Element, Name: "id" } => "string", - // _ => TypeName - // }; - // } - // } - // - // object? ObjectValue - // { - // get - // { - // if (this is not PrimitiveType { ObjectValue: { } ov }) return null; - // if (ov == _lastCachedValue) return _value; - // _value = ToITypedElementValue(); - // _lastCachedValue = ov; - // - // return _value; - // } - // } - // - // string ITypedElement.Location => - // (ScopeInfo.Index, ScopeInfo.Parent) switch - // { - // // if we have an index, write it - // ({ } idx, { } parent) => $"{parent.Location}.{ScopeInfo.Name}[{idx}]", - // // if we do not, write 0 as idx - // (_, { } parent) => $"{parent.Location}.{ScopeInfo.Name}[0]", - // // if we have neither, we are the root. - // _ => $"{ScopeInfo.Name}" - // }; - // - // [TemporarilyChanged] // We need to use Children for now to preserve scope access, but we would really prefer poco-accesses here. When we refactor, we should change this back. - // bool IScopedNode.TryResolveBundleEntry(string fullUrl, [NotNullWhen(true)] out IScopedNode? result) - // { - // result = this is Bundle b ? (b as IScopedNode) - // .Children("entry").FirstOrDefault(entry => entry.Children("fullUrl") - // .SingleOrDefault()?.Value is string url && url == fullUrl)? - // .Children("resource").SingleOrDefault() : null; - // return result is not null; - // } - // - // [TemporarilyChanged] // We need to use Children for now to preserve scope access, but we would really prefer poco-accesses here. When we refactor, we should change this back. - // bool IScopedNode.TryResolveContainedEntry(string id, [NotNullWhen(true)] out IScopedNode? result) - // { - // result = this is DomainResource dr ? (dr as IScopedNode).Children("contained").FirstOrDefault(contained => contained.Children("id").SingleOrDefault()?.Value is string containedId && $"#{containedId}" == id) : null; - // return result is not null; - // } - // - // IElementDefinitionSummary? ITypedElement.Definition => null; - // - // #endregion - // - // #region IScopedNode - // - // string IScopedNode.Name => ScopeInfo.Name; - // - // NodeType IScopedNode.Type => - // this switch - // { - // Bundle => NodeType.Bundle | NodeType.Resource, - // PrimitiveType => NodeType.Primitive, - // DomainResource => NodeType.DomainResource | NodeType.Resource, - // Resource => NodeType.Resource, - // ResourceReference or Canonical or CodeableReference => NodeType.Reference, - // Quantity => NodeType.Quantity, - // _ => 0 - // }; - // - // object? IScopedNode.Value - // { - // get - // { - // if (this is not PrimitiveType { ObjectValue: { } ov }) return null; - // if (ov == _lastCachedValue) return _value; - // _value = ToITypedElementValue(); - // _lastCachedValue = ov; - // - // return _value; - // } - // } - // - // string IScopedNode.Location => - // (ScopeInfo.Index, ScopeInfo.Parent) switch - // { - // // if we have an index, write it - // ({ } idx, { } parent) => $"{parent.Location}.{ScopeInfo.Name}[{idx}]", - // // if we do not, write 0 as idx - // (_, { } parent) => $"{parent.Location}.{ScopeInfo.Name}[0]", - // // if we have neither, we are the root. - // _ => $"{ScopeInfo.Name}" - // }; - // - // IScopedNode? IScopedNode.Parent => ScopeInfo.Parent; - // - // IEnumerable IScopedNode.Children(string? name) => this.GetElementPairs() - // .Where(ep => (name == null || name == ep.Key)) - // .SelectMany, Base>(ep => - // (ep.Key, ep.Value) switch - // { - // (_, Base b) => (IEnumerable)[b.WithScopeInfo(new ScopeInformation(this, ep.Key, null))], - // (_, IEnumerable list) => list.Select((item, idx) => item.WithScopeInfo(new ScopeInformation(this, ep.Key, idx))), - // ("url", string s) when this is Extension => [new FhirUri(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))], - // ("id", string s) when this is Element => [new FhirString(s).WithScopeInfo(new ScopeInformation(this, ep.Key, null))], - // ("value", _) => [], - // _ => throw new InvalidOperationException("Unexpected system primitive in child list") - // } - // ); - // - // #endregion -} - -#endif \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Model/DynamicDataType.cs b/src/Hl7.Fhir.Base/Model/DynamicDataType.cs index 91e8ff7844..fda7e5abfb 100644 --- a/src/Hl7.Fhir.Base/Model/DynamicDataType.cs +++ b/src/Hl7.Fhir.Base/Model/DynamicDataType.cs @@ -41,7 +41,7 @@ protected internal override Base DeepCopyInternal() /// [Serializable] [DataContract] -[FhirType("DynamicDataType","http://fire.ly/fhir/StructureDefinition/DynamicResource")] +[FhirType("DynamicResource","http://fire.ly/fhir/StructureDefinition/DynamicResource")] public class DynamicResource : Resource, IDynamicType { public string? DynamicTypeName { get; set; } diff --git a/src/Hl7.Fhir.Base/Model/IScopedNode.cs b/src/Hl7.Fhir.Base/Model/IScopedNode.cs index 37ead4a208..9daab8dac4 100644 --- a/src/Hl7.Fhir.Base/Model/IScopedNode.cs +++ b/src/Hl7.Fhir.Base/Model/IScopedNode.cs @@ -1,29 +1,8 @@ using Hl7.Fhir.ElementModel; -using Hl7.Fhir.Rest; using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Transactions; namespace Hl7.Fhir.Model; -#nullable enable - -[Flags] -public enum NodeType -{ - Resource = 1, - Bundle = 1 << 1, - DomainResource = 1 << 2, - Primitive = 1 << 3, - Reference = 1 << 4, - Quantity = 1 << 5, -} - /// /// An element within a tree of typed FHIR data with also a parent element. /// @@ -31,71 +10,5 @@ public enum NodeType /// 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 -public partial interface IScopedNode : ITypedElement, IShortPathGenerator -#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; } - - /// - /// A flag enum indicating the type of the node. - /// - [TemporarilyChanged] // will be removed in the very near future - NodeType Type { get; } - - new IEnumerable Children(string? name = null); - - /// - /// Resolves a reference from this node to another resource. This node should be a Bundle. - /// - /// - /// - /// - public bool TryResolveBundleEntry(string fullUrl, [NotNullWhen(true)] out IScopedNode? result); - - /// - /// Resolves a reference from this node to another resource. This node should be a DomainResource. - /// - /// - /// - /// - public bool TryResolveContainedEntry(string id, [NotNullWhen(true)] out IScopedNode? result); - - /// - /// Resolve a resource reference within the context of this node given a url (for bundles) or id (for contained). - /// - /// The relative URL to resolve. - /// Contains the referenced instance, or null if the operation failed - /// Does not create a copy. The resolved resource will be part of the IScopedNode-tree that was passed to this function - /// t - public bool TryResolveLocalReference(string url, [NotNullWhen(true)] out IScopedNode? result) - { - for(var scan = this; scan is not null; scan = scan.Parent) - { - if (scan.Type.HasFlag(NodeType.Bundle)) // if we do not find it in the closest bundle, the reference is invalid - { - return scan.TryResolveBundleEntry(url, out result); - } - - if (scan.Type.HasFlag(NodeType.DomainResource) && scan.TryResolveContainedEntry(url, out result)) - { - // if we encounter a DomainResource, try to resolve the contained reference. - // If it fails, higher domain resources could still contain it! - return true; - } - - if (scan.Children("id").FirstOrDefault()?.Value as string == url[1..]) - { - // if we encounter a resource with the correct id, return it - result = scan; - return true; - } - } - - result = null; - return false; - } -} \ No newline at end of file +[Obsolete("use PocoNode instead")] +public interface IScopedNode : ITypedElement; \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Model/ScopedNodeExtensions.cs b/src/Hl7.Fhir.Base/Model/ScopedNodeExtensions.cs index c08f0e4168..e69de29bb2 100644 --- a/src/Hl7.Fhir.Base/Model/ScopedNodeExtensions.cs +++ b/src/Hl7.Fhir.Base/Model/ScopedNodeExtensions.cs @@ -1,133 +0,0 @@ -using Hl7.Fhir.ElementModel; -using Hl7.Fhir.Rest; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml.Schema; - -namespace Hl7.Fhir.Model; - -#nullable enable - -public static class ScopedNodeExtensions -{ - internal static IEnumerable Parents(this IScopedNode node) - { - for(var scan = node.Parent; scan is not null; scan = scan.Parent) - { - yield return scan; - } - } - - // wrote this, but it never gets picked over the ElementNodeExtensions version which is a shame. Let's keep it here for now. - public static IEnumerable Children(this IEnumerable node, string? name = null) => - node.SelectMany(n => n.Children(name)); - - private static IScopedNode? getContainer(this IScopedNode node) - { - var scan = node; - while(scan is not (null or { Name: "contained" })) - { - scan = scan.Parent; // navigate up to "contained" - } - - return scan?.Parent; // return the container (DomainResource around contained) - } - - /// - /// Resolve the given reference within the context of the given node. This node should be of type reference. - /// - /// A node representing a reference - /// An external resolver - /// - public static IScopedNode? Resolve(this IScopedNode? node, Func? externalResolver = null) - { - if (node is null) return null; - - string? url = node switch - { - { Value: string s } => s, // canonicals can be references - { Type: NodeType.Reference } => node.ParseResourceReference().Reference, - _ => throw new ArgumentException($"Error occurred during reference resolution: Parameter {nameof(node)} is not a reference.") - }; - - return url is null ? null : Resolve(node, url, externalResolver); - } - - public static IScopedNode? Resolve(this IScopedNode? node, string url, Func? externalResolver = null) - { - if (node is null) return null; - - if(url == "#") return node.getContainer(); - - var identity = node.MakeAbsolute(new ResourceIdentity(url)); - if (node.TryResolveLocalReference(identity.ToString(), out var localResult)) return localResult; - - return externalResolver?.Invoke(url); - } - - /// - /// Extract the %resource variable from this IScopedNode - /// - internal static IScopedNode? GetResourceContext(this IScopedNode node) => node switch - { - { Parent: null } => node, // if parent is null, do not go further - { Type: var type } when type.HasFlag(NodeType.Resource) => node, // if resource, return itself - _ => node?.Parent?.GetResourceContext() // otherwise, go to parent - }; - - /// - /// Extract the %rootResource variable from this IScopedNode - /// - internal static IScopedNode? GetRootResourceContext(this IScopedNode node) => node.GetResourceContext() switch - { - { Name : "contained" } containedResource => containedResource.Parent!, // if contained, return container - { } resource => resource, // otherwise return %resource - _ => null - }; - - internal static string? FindFullUrl(this IScopedNode node) - { - if(node.Name == "entry") return node.Children("fullUrl").FirstOrDefault()?.Value as string; - - return node.Parent?.FindFullUrl(); - } - - /// - /// Turn a relative reference into an absolute url, based on the fullUrl of the parent resource - /// - /// See https://www.hl7.org/fhir/bundle.html#references for more information - internal static ResourceIdentity MakeAbsolute(this IScopedNode node, ResourceIdentity identity) - { - if (!identity.IsRelativeRestUrl) return identity; - // Relocate the relative url on the base given in the fullUrl of the entry (if applicable) - var fullUrl = node.FindFullUrl(); - - if (fullUrl == null) return identity; - - var parentIdentity = new ResourceIdentity(fullUrl); - - if (parentIdentity.IsAbsoluteRestUrl) - identity = identity.WithBase(parentIdentity.BaseUri); - else if (parentIdentity.IsUrn) - identity = new ResourceIdentity($"{parentIdentity}/{identity.Id}"); - - // Return the identity - will remain relative if we did not find a fullUrl - - return identity; - } - - public static string MakeAbsolute(this IScopedNode node, string reference) => - node.MakeAbsolute(new ResourceIdentity(reference)).ToString(); - - internal static IScopedNode? GetParentResource(this IScopedNode node) => node.Parents().FirstOrDefault(parentNode => parentNode.Type.HasFlag(NodeType.Resource)); - - internal static string GetLocalLocation(this IScopedNode node) => - node.Parent is null - ? node.Location - : $"{node.GetParentResource()!.InstanceType}.{node.Location[(node.GetParentResource()!.Location.Length + 1)..]}"; - - public static IEnumerable ContainedResources(this IScopedNode node) => node.Children("contained"); - - public static IEnumerable BundledResources(this IScopedNode node) => node.Children("entry"); -} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Model/Time.cs b/src/Hl7.Fhir.Base/Model/Time.cs index ed44b40a98..c675cc8494 100644 --- a/src/Hl7.Fhir.Base/Model/Time.cs +++ b/src/Hl7.Fhir.Base/Model/Time.cs @@ -45,7 +45,7 @@ public Time(int hour, int minute, int second) : this(string.Format(CultureInfo.I { // Nothing } - + /// /// Takes the hour, minute and second of a given in the indicated timezone, and uses this /// to construct a new Time. diff --git a/src/Hl7.Fhir.Base/Serialization/FhirXmlBuilderExtensions.cs b/src/Hl7.Fhir.Base/Serialization/FhirXmlBuilderExtensions.cs deleted file mode 100644 index 4789df431d..0000000000 --- a/src/Hl7.Fhir.Base/Serialization/FhirXmlBuilderExtensions.cs +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2018, 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://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE - */ - - -using Hl7.Fhir.ElementModel; -using Hl7.Fhir.Introspection; -using Hl7.Fhir.Model; -using Hl7.Fhir.Utility; -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; -using PocoNode = Hl7.Fhir.ElementModel.PocoNode; - -namespace Hl7.Fhir.Serialization; - -public static class FhirXmlBuilderExtensions -{ - /// - /// Serializes an instance into FHIR Xml. - /// - /// The instance to serialize. - /// The to write the serialized data to. - /// Since has no type information, this function will throw unless - /// the originated from parsing using . - public static void WriteTo(this ISourceNode source, XmlWriter writer) => - new FhirXmlBuilder().Build(source).writeTo(writer); - - /// - public static async Task WriteToAsync(this ISourceNode source, XmlWriter destination) => - await new FhirXmlBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); - - /// - /// Serializes an instance to FHIR Xml. - /// - /// The instance to serialize. - /// The to write the serialized data to. - public static void WriteTo(this ITypedElement source, XmlWriter writer) => - new FhirXmlBuilder().Build(source).writeTo(writer); - - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task WriteToAsync(this ITypedElement source, XmlWriter destination) => - await new FhirXmlBuilder().Build(source).writeToAsync(destination).ConfigureAwait(false); - - /// - /// Serializes an instance into a . - /// - /// The instance to serialize. - /// Since has no type information, this function will throw unless - /// the originated from parsing using . - public static XDocument ToXDocument(this ISourceNode source) => - new FhirXmlBuilder().Build(source); - - /// - /// Serializes an instance into a - /// - /// The instance to serialize. - public static XDocument ToXDocument(this ITypedElement source) => - new FhirXmlBuilder().Build(source); - - /// - /// Serializes an instance into a FHIR Xml string. - /// - /// The instance to serialize. - /// Formats and indents the serialized Xml. - /// Since has no type information, this function will throw unless - /// the originated from parsing using . - public static string ToXml(this ISourceNode source, bool pretty = false) - => SerializationUtil.WriteXmlToString(source.WriteTo, pretty); - - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task ToXmlAsync(this ISourceNode source, bool pretty = false) - => await SerializationUtil.WriteXmlToStringAsync(source.WriteToAsync, pretty).ConfigureAwait(false); - - /// - /// Serializes an instance into a FHIR Xml string. - /// - /// The instance to serialize. - /// Formats and indents the serialized Xml. - public static string ToXml(this ITypedElement source, bool pretty = false) - { - if (source is not PocoNode node) - return SerializationUtil.WriteXmlToString(source.WriteTo, pretty); - - return serializePocoNode(node, pretty); - } - - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task ToXmlAsync(this ITypedElement source, bool pretty = false) - { - if (source is not PocoNode node) - return await SerializationUtil.WriteXmlToStringAsync(source.WriteToAsync, pretty).ConfigureAwait(false); - - return serializePocoNode(node, pretty); - } - - private static string serializePocoNode(PocoNode pn, bool pretty) - { - var serializer = new BaseFhirXmlSerializer(pn.FindInspector() ?? ModelInspector.ForType(pn.Poco.GetType())); - - // If we are serializing a subtree of a resource, then if the current node is a datatype or a nested resource, - // we need to pick a name for this root element. - var pickElementName = pn.Poco is not Resource || pn.Parent is not null; - var rootName = pickElementName ? pn.Name : null; - - return serializer.SerializeToString(pn.Poco, pretty, rootName: rootName); - } - - /// - /// Serializes an instance into a FHIR Xml byte array. - /// - /// The instance to serialize. - /// Formats and indents the serialized Xml. - public static byte[] ToXmlBytes(this ITypedElement source, bool pretty = false) - => SerializationUtil.WriteXmlToBytes(source.WriteTo, pretty); - - /// - [Obsolete("Async support will be removed in the next major release, please use the non-async version instead")] - public static async Task ToXmlBytesAsync(this ITypedElement source, bool pretty = false) - => await SerializationUtil.WriteXmlToBytesAsync(source.WriteToAsync, pretty).ConfigureAwait(false); - - private static void writeTo(this XDocument doc, XmlWriter destination) - { - if (doc.Root != null) - doc.WriteTo(destination); - - destination.Flush(); - } - - private static async Task writeToAsync(this XDocument doc, XmlWriter destination) - { - if (doc.Root != null) - await doc.WriteToAsync(destination, CancellationToken.None); - - await destination.FlushAsync().ConfigureAwait(false); - } -} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Serialization/JsonDocumentExtensions.cs b/src/Hl7.Fhir.Base/Serialization/JsonDocumentExtensions.cs new file mode 100644 index 0000000000..0b00c10fe9 --- /dev/null +++ b/src/Hl7.Fhir.Base/Serialization/JsonDocumentExtensions.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018, 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://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE + */ + + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Threading.Tasks; + +namespace Hl7.Fhir.Serialization; + +internal static class JsonDocumentExtensions +{ + internal static void writeTo(this JObject root, JsonWriter destination) + { + root.WriteTo(destination); + destination.Flush(); + } + + internal static async Task writeToAsync(this JObject root, JsonWriter destination) + { + await root.WriteToAsync(destination).ConfigureAwait(false); + await destination.FlushAsync().ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Base/Serialization/XmlDocumentExtensions.cs b/src/Hl7.Fhir.Base/Serialization/XmlDocumentExtensions.cs new file mode 100644 index 0000000000..31477fe312 --- /dev/null +++ b/src/Hl7.Fhir.Base/Serialization/XmlDocumentExtensions.cs @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, 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://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE + */ + + +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; + +namespace Hl7.Fhir.Serialization; + +internal static class XDocumentExtensions +{ + internal static void writeTo(this XDocument doc, XmlWriter destination) + { + if (doc.Root != null) + doc.WriteTo(destination); + + destination.Flush(); + } + + internal static async Task writeToAsync(this XDocument doc, XmlWriter destination) + { + if (doc.Root != null) + await doc.WriteToAsync(destination, CancellationToken.None); + + await destination.FlushAsync().ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Conformance/CompatibilitySuppressions.xml b/src/Hl7.Fhir.Conformance/CompatibilitySuppressions.xml index e1badae5f7..0483fa0362 100644 --- a/src/Hl7.Fhir.Conformance/CompatibilitySuppressions.xml +++ b/src/Hl7.Fhir.Conformance/CompatibilitySuppressions.xml @@ -1,5 +1,5 @@  - + CP0002 diff --git a/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs b/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs index b4bd16a523..6f74a1bf6b 100644 --- a/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs +++ b/src/Hl7.Fhir.ElementModel.Shared.Tests/ElementNodeTests.cs @@ -11,6 +11,7 @@ using FluentAssertions; using Hl7.Fhir.ElementModel; +using Hl7.Fhir.Introspection; using Hl7.Fhir.Model; using Hl7.Fhir.Serialization; using Hl7.Fhir.Specification; @@ -38,6 +39,7 @@ private ElementNode createPatient() var patientRoot = ElementNode.Root(_provider, "Patient"); var containedObs = ElementNode.Root(_provider, "Observation", "contained"); + containedObs.Add(_provider, "value", true, "boolean"); patientRoot.Add(_provider, containedObs); @@ -72,6 +74,9 @@ public void TestFpNavigate() var obs = ElementNode.Root(_provider, "Observation"); obs.Add(_provider, "id", "test"); + patient.AddAnnotation(ModelInfo.ModelInspector); + obs.AddAnnotation(ModelInfo.ModelInspector); + patient.Add(_provider, obs, "contained"); // Select on the root of the resource, path should match with resource name included diff --git a/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeOnBaseTests.cs b/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeOnBaseTests.cs index e08d84e496..aab28e51c2 100644 --- a/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeOnBaseTests.cs +++ b/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeOnBaseTests.cs @@ -17,7 +17,6 @@ namespace Hl7.Fhir.ElementModel.Tests { [TestClass] - [TemporarilyChanged] // We should refactor these tests against PocoElementNode2 public class ScopedNodeOnBaseTests { private PocoNode _bundleNode; @@ -33,7 +32,6 @@ public void SetupSource() } [TestMethod] - [TemporarilyChanged] // This test will be improved once ContainedResources and BundledResources are implemented on PocoNodeOrList, not IScopedNode public void GetContainedAndBundledResources() { Assert.AreEqual(0, _bundleNode!.ContainedResources().Count()); @@ -52,7 +50,7 @@ public void GetContainedAndBundledResources() var entry6 = entries[6].Resource; Assert.AreEqual(2, entry6!.ToPocoNode().ContainedResources().Count()); Assert.IsFalse(entry6.ToPocoNode().BundledResources().Any()); - Assert.AreEqual("orgY", (entry6.ToPocoNode().ContainedResources().Skip(1).First().Children("id").First().Value)); + Assert.AreEqual("orgY", (entry6.ToPocoNode().ContainedResources().Skip(1).First().Child("id")?.Value)); } [TestMethod] @@ -60,35 +58,35 @@ public void GetFullUrl() { var entries = _bundleNode!.BundledResources().ToList(); - Assert.AreEqual("http://example.org/fhir/Patient/b", entries[3].FindFullUrl()); + Assert.AreEqual("http://example.org/fhir/Patient/b", entries[3].Child("resource")?.FindFullUrl()); - IScopedNode entry3 = entries[3].Children("resource").FirstOrDefault(); - entry3 = entry3?.Children("managingOrganization").FirstOrDefault(); + var entry3 = entries[3].Child("resource"); + entry3 = entry3?.FlatChildren("managingOrganization").FirstOrDefault(); Assert.IsNotNull(entry3); - entry3 = entry3.Children("reference").FirstOrDefault(); + entry3 = entry3.FlatChildren("reference").FirstOrDefault(); Assert.IsNotNull(entry3); - Assert.AreEqual(entries[3].FindFullUrl(), entry3.FindFullUrl()); + Assert.AreEqual(entries[3].Child("resource")?.FindFullUrl(), entry3.FindFullUrl()); Assert.AreEqual(entry3.GetParentResource()!.FindFullUrl(), entry3.FindFullUrl()); - var entry6 = entries[6].Children("resource").FirstOrDefault(); + var entry6 = entries[6].FlatChildren("resource").FirstOrDefault(); entry6 = entry6?.ContainedResources().Skip(1).FirstOrDefault(); Assert.IsNotNull(entry6); - Assert.AreEqual("orgY", entry6.Children("id").FirstOrDefault()?.Value); - Assert.AreEqual(entries[6].FindFullUrl(), entry6.FindFullUrl()); + Assert.AreEqual("orgY", entry6.FlatChildren("id").FirstOrDefault()?.GetValue()); + Assert.AreEqual(entries[6].Child("resource")?.FindFullUrl(), entry6.FindFullUrl()); Assert.AreEqual(entry6.GetParentResource()!.FindFullUrl(), entry6.FindFullUrl()); } [TestMethod] public void TestMakeAbsolute() { - var inner0 = _bundleNode!.BundledResources().First().Children("resource").Children("active").SingleOrDefault() as IScopedNode; + var inner0 = _bundleNode!.BundledResources().First().Child("resource")?.FlatChildren("active").SingleOrDefault(); Assert.IsNotNull(inner0); Assert.AreEqual("http://example.org/fhir/Patient/3", inner0.MakeAbsolute("Patient/3")); Assert.AreEqual("http://nu.nl/myPat/3x", inner0.MakeAbsolute("http://nu.nl/myPat/3x")); Assert.AreEqual("http://example.org/fhir/Organization/5", inner0.MakeAbsolute("http://example.org/fhir/Organization/5")); - var inner1 = _bundleNode.BundledResources().Skip(1).First().Children("resource").Children("active").SingleOrDefault() as IScopedNode; + var inner1 = _bundleNode.BundledResources().Skip(1).First().Child("resource")?.FlatChildren("active").SingleOrDefault(); Assert.AreEqual("urn:uuid:04121321-4af5-424c-a0e1-ed3aab1c349d/3", inner1!.MakeAbsolute("Patient/3")); Assert.AreEqual("http://nu.nl/myPat/3x", inner1!.MakeAbsolute("http://nu.nl/myPat/3x")); @@ -100,33 +98,33 @@ public void TestContainedCanResolveToContainer() { Assert.IsNull(_bundleNode!.Resolve("#")); - var patient = _bundleNode!.BundledResources().Skip(6).First().Children("resource").First(); + var patient = _bundleNode!.BundledResources().Skip(6).First().Child("resource")?.First(); Assert.IsNull(patient.Resolve("#")); - var containedOrg = patient.ContainedResources().First(); - Assert.AreEqual("Patient", containedOrg.Resolve("#")!.InstanceType); + var containedOrg = patient?.ContainedResources().First(); + Assert.AreEqual("Patient", containedOrg.Resolve("#")!.Poco.TypeName); - var containedId = containedOrg.Children("id").First(); - Assert.AreEqual("Patient", containedId.Resolve("#")!.InstanceType); + var containedId = containedOrg?.Child("id")?.First(); + Assert.AreEqual("Patient", containedId.Resolve("#")!.Poco.TypeName); } [TestMethod] public void TestResolve() { - IScopedNode inner7 = (_bundleNode!.BundledResources().Skip(6).First().Children("resource").Children("managingOrganization").SingleOrDefault() as IScopedNode)!; - - Assert.AreEqual("Bundle.entry[6].resource[0]", inner7.Resolve("http://example.org/fhir/Patient/e")!.Location); - Assert.AreEqual("Bundle.entry[6].resource[0].contained[1]", inner7.Resolve("#orgY")!.Location); - Assert.AreEqual("Bundle.entry[6].resource[0]", inner7.Resolve("#e")!.Location); - Assert.AreEqual("Bundle.entry[5].resource[0]", inner7.Resolve("http://example.org/fhir/Patient/d")!.Location); - Assert.AreEqual("Bundle.entry[5].resource[0]", inner7.Resolve("Patient/d")!.Location); - Assert.AreEqual("Bundle.entry[1].resource[0]", inner7.Resolve("urn:uuid:04121321-4af5-424c-a0e1-ed3aab1c349d")!.Location); + PocoNode inner7 = (_bundleNode!.BundledResources().Skip(6).First().Child("resource")?.FlatChildren("managingOrganization").Single()); + + Assert.AreEqual("Bundle.entry[6].resource[0]", inner7.Resolve("http://example.org/fhir/Patient/e")!.GetLocation()); + Assert.AreEqual("Bundle.entry[6].resource[0].contained[1]", inner7.Resolve("#orgY")!.GetLocation()); + Assert.AreEqual("Bundle.entry[6].resource[0]", inner7.Resolve("#e")!.GetLocation()); + Assert.AreEqual("Bundle.entry[5].resource[0]", inner7.Resolve("http://example.org/fhir/Patient/d")!.GetLocation()); + Assert.AreEqual("Bundle.entry[5].resource[0]", inner7.Resolve("Patient/d")!.GetLocation()); + Assert.AreEqual("Bundle.entry[1].resource[0]", inner7.Resolve("urn:uuid:04121321-4af5-424c-a0e1-ed3aab1c349d")!.GetLocation()); Assert.IsNull(inner7.Resolve("#d")); Assert.IsNull(inner7.Resolve("http://nu.nl/3")); - Assert.AreEqual("Bundle.entry[6].resource[0].contained[1]", inner7.Resolve()!.Location); - Assert.IsTrue(inner7.Children("reference").Any()); - Assert.AreEqual("Bundle.entry[6].resource[0].contained[1]", inner7.Children("reference").First().Resolve()!.Location); + Assert.AreEqual("Bundle.entry[6].resource[0].contained[1]", inner7.Resolve()!.GetLocation()); + Assert.IsTrue(inner7!.Child("reference") is not null); + Assert.AreEqual("Bundle.entry[6].resource[0].contained[1]", inner7.Child("reference")!.First().Resolve()!.GetLocation()); string lastUrlResolved = ""; @@ -135,7 +133,7 @@ public void TestResolve() Assert.IsNull(inner7.Resolve("http://nu.nl/3", externalResolve)); Assert.AreEqual("http://nu.nl/3", lastUrlResolved); - IScopedNode externalResolve(string url) + PocoNode externalResolve(string url) { lastUrlResolved = url; return null; @@ -144,13 +142,12 @@ IScopedNode externalResolve(string url) [TestMethod] - [Experimental("ExperimentalApi")] public void Bundle_WithEntryWithoutFullUrl_ShouldNotThrow() { - var bundle = new Bundle() { Type = Bundle.BundleType.Batch, Entry = [new Bundle.EntryComponent() { Resource = new Patient() }]}.ToTypedElement().ToScopedNode(); + var bundle = new Bundle() { Type = Bundle.BundleType.Batch, Entry = [new Bundle.EntryComponent() { Resource = new Patient() }]}.ToPocoNode(); var enumerate = () => bundle.BundledResources(); - enumerate.Should().NotThrow().Subject.Should().ContainSingle(c => !c.Children("fullUrl").Any()); + enumerate.Should().NotThrow().Subject.Should().ContainSingle(c => c.Child("fullUrl") == null); } } } \ No newline at end of file diff --git a/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs b/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs index 2a6ac98e42..e1e1d8599f 100644 --- a/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs +++ b/src/Hl7.Fhir.ElementModel.Shared.Tests/ScopedNodeTests.cs @@ -182,7 +182,7 @@ public void TestContainedCanResolveToContainer() { Assert.IsNull(_bundleNode!.Resolve("#")); - var patient = _bundleNode!.Children("entry").Skip(6).First().Children("resource").First(); + var patient = _bundleNode!.Children("entry").Skip(6).Children("resource").First(); Assert.IsNull(patient.Resolve("#")); var containedOrg = patient.Children("contained").First(); @@ -217,7 +217,7 @@ public void TestResolve() Assert.IsNull(inner7.Resolve("http://nu.nl/3", externalResolve)); Assert.AreEqual("http://nu.nl/3", lastUrlResolved); - IScopedNode? externalResolve(string url) + ITypedElement? externalResolve(string url) { lastUrlResolved = url; return null; @@ -271,15 +271,17 @@ static bool CCDATypeNameMapper(string typeName, out string canonical) Assert.AreEqual("xhtml", typedElements.First().InstanceType); Assert.AreEqual("Section.text[0]", typedElements.First().Location); Assert.IsNotNull(typedElements.First().Value); + + } [TestMethod] public void Bundle_WithEntryWithoutFullUrl_ShouldNotThrow() { - var bundle = new Bundle() { Type = Bundle.BundleType.Batch, Entry = [new Bundle.EntryComponent() { Resource = new Patient() }]}; + var bundle = new Bundle() { Type = Bundle.BundleType.Batch, Entry = [new Bundle.EntryComponent() { Resource = new Patient() }]}.ToTypedElement().ToScopedNode(); - var enumerate = () => bundle.ToPocoNode().BundledResources(); - enumerate.Should().NotThrow().Subject.Should().ContainSingle(c => !c.Children("fullUrl").Any()); + var enumerate = () => bundle.BundledResources(); + enumerate.Should().NotThrow().Subject.Should().ContainSingle(c => c.FullUrl == null); } private class CCDAResourceResolver : IAsyncResourceResolver diff --git a/src/Hl7.Fhir.R4/CompatibilitySuppressions.xml b/src/Hl7.Fhir.R4/CompatibilitySuppressions.xml index bef5f28bb2..e30a9d3be1 100644 --- a/src/Hl7.Fhir.R4/CompatibilitySuppressions.xml +++ b/src/Hl7.Fhir.R4/CompatibilitySuppressions.xml @@ -1,6 +1,13 @@  - + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/net8.0/Hl7.Fhir.R4.dll + lib/net8.0/Hl7.Fhir.R4.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -8,6 +15,13 @@ lib/net8.0/Hl7.Fhir.R4.dll true + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.R4.dll + lib/netstandard2.1/Hl7.Fhir.R4.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -15,6 +29,13 @@ lib/netstandard2.1/Hl7.Fhir.R4.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/net8.0/Hl7.Fhir.R4.dll + lib/net8.0/Hl7.Fhir.R4.dll + true + CP0002 M:Hl7.Fhir.Rest.FhirClientOperationsExtensions.CapabilityStatement(Hl7.Fhir.Rest.BaseFhirClient,System.Nullable{Hl7.Fhir.Rest.SummaryType}) @@ -267,6 +288,13 @@ lib/net8.0/Hl7.Fhir.R4.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/netstandard2.1/Hl7.Fhir.R4.dll + lib/netstandard2.1/Hl7.Fhir.R4.dll + true + CP0002 M:Hl7.Fhir.Rest.FhirClientOperationsExtensions.CapabilityStatement(Hl7.Fhir.Rest.BaseFhirClient,System.Nullable{Hl7.Fhir.Rest.SummaryType}) diff --git a/src/Hl7.Fhir.R4B/CompatibilitySuppressions.xml b/src/Hl7.Fhir.R4B/CompatibilitySuppressions.xml index e096fa6734..bb07989e1c 100644 --- a/src/Hl7.Fhir.R4B/CompatibilitySuppressions.xml +++ b/src/Hl7.Fhir.R4B/CompatibilitySuppressions.xml @@ -1,6 +1,13 @@  - + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/net8.0/Hl7.Fhir.R4B.dll + lib/net8.0/Hl7.Fhir.R4B.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -8,6 +15,13 @@ lib/net8.0/Hl7.Fhir.R4B.dll true + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.R4B.dll + lib/netstandard2.1/Hl7.Fhir.R4B.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -15,6 +29,13 @@ lib/netstandard2.1/Hl7.Fhir.R4B.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/net8.0/Hl7.Fhir.R4B.dll + lib/net8.0/Hl7.Fhir.R4B.dll + true + CP0002 M:Hl7.Fhir.Rest.FhirClientOperationsExtensions.CapabilityStatement(Hl7.Fhir.Rest.BaseFhirClient,System.Nullable{Hl7.Fhir.Rest.SummaryType}) @@ -267,6 +288,13 @@ lib/net8.0/Hl7.Fhir.R4B.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/netstandard2.1/Hl7.Fhir.R4B.dll + lib/netstandard2.1/Hl7.Fhir.R4B.dll + true + CP0002 M:Hl7.Fhir.Rest.FhirClientOperationsExtensions.CapabilityStatement(Hl7.Fhir.Rest.BaseFhirClient,System.Nullable{Hl7.Fhir.Rest.SummaryType}) diff --git a/src/Hl7.Fhir.R5/CompatibilitySuppressions.xml b/src/Hl7.Fhir.R5/CompatibilitySuppressions.xml index 0867ce9291..31beff887b 100644 --- a/src/Hl7.Fhir.R5/CompatibilitySuppressions.xml +++ b/src/Hl7.Fhir.R5/CompatibilitySuppressions.xml @@ -1,6 +1,13 @@  - + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/net8.0/Hl7.Fhir.R5.dll + lib/net8.0/Hl7.Fhir.R5.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -8,6 +15,13 @@ lib/net8.0/Hl7.Fhir.R5.dll true + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.R5.dll + lib/netstandard2.1/Hl7.Fhir.R5.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -15,6 +29,13 @@ lib/netstandard2.1/Hl7.Fhir.R5.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/net8.0/Hl7.Fhir.R5.dll + lib/net8.0/Hl7.Fhir.R5.dll + true + CP0002 M:Hl7.Fhir.Rest.FhirClientOperationsExtensions.CapabilityStatement(Hl7.Fhir.Rest.BaseFhirClient,System.Nullable{Hl7.Fhir.Rest.SummaryType}) @@ -267,6 +288,13 @@ lib/net8.0/Hl7.Fhir.R5.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/netstandard2.1/Hl7.Fhir.R5.dll + lib/netstandard2.1/Hl7.Fhir.R5.dll + true + CP0002 M:Hl7.Fhir.Rest.FhirClientOperationsExtensions.CapabilityStatement(Hl7.Fhir.Rest.BaseFhirClient,System.Nullable{Hl7.Fhir.Rest.SummaryType}) diff --git a/src/Hl7.Fhir.STU3/CompatibilitySuppressions.xml b/src/Hl7.Fhir.STU3/CompatibilitySuppressions.xml index 82541f7bae..9f7bf48c1a 100644 --- a/src/Hl7.Fhir.STU3/CompatibilitySuppressions.xml +++ b/src/Hl7.Fhir.STU3/CompatibilitySuppressions.xml @@ -1,6 +1,13 @@  - + + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/net8.0/Hl7.Fhir.STU3.dll + lib/net8.0/Hl7.Fhir.STU3.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -8,6 +15,13 @@ lib/net8.0/Hl7.Fhir.STU3.dll true + + CP0001 + T:Hl7.Fhir.ElementModel.PocoBuilderExtensions + lib/netstandard2.1/Hl7.Fhir.STU3.dll + lib/netstandard2.1/Hl7.Fhir.STU3.dll + true + CP0001 T:Hl7.Fhir.ElementModel.TypedElementExtensions @@ -15,6 +29,13 @@ lib/netstandard2.1/Hl7.Fhir.STU3.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/net8.0/Hl7.Fhir.STU3.dll + lib/net8.0/Hl7.Fhir.STU3.dll + true + CP0002 M:Hl7.Fhir.Model.ElementDefinition.get_Name @@ -295,6 +316,13 @@ lib/net8.0/Hl7.Fhir.STU3.dll true + + CP0002 + M:Hl7.Fhir.FhirPath.FhirPathExtensions.ToFhirPathResolver(System.Func{System.String,Hl7.Fhir.ElementModel.PocoNode}) + lib/netstandard2.1/Hl7.Fhir.STU3.dll + lib/netstandard2.1/Hl7.Fhir.STU3.dll + true + CP0002 M:Hl7.Fhir.Model.ElementDefinition.get_Name diff --git a/src/Hl7.Fhir.Serialization.Shared.Tests/ParseDemoPatientXmlTyped.cs b/src/Hl7.Fhir.Serialization.Shared.Tests/ParseDemoPatientXmlTyped.cs index 9fe41e8470..875abd423d 100644 --- a/src/Hl7.Fhir.Serialization.Shared.Tests/ParseDemoPatientXmlTyped.cs +++ b/src/Hl7.Fhir.Serialization.Shared.Tests/ParseDemoPatientXmlTyped.cs @@ -91,7 +91,7 @@ public void HasLineNumbersTypedXml() public void CheckBundleEntryNavigation() { var bundle = File.ReadAllText(Path.Combine("TestData", "BundleWithOneEntry.xml")); - var node = getXmlNode(bundle); + var node = getXmlNode(bundle, new FhirXmlParsingSettings{PermissiveParsing = true}); ParseDemoPatient.CheckBundleEntryNavigation(node); } diff --git a/src/Hl7.Fhir.Serialization.Shared.Tests/RoundtripAllSerializers.cs b/src/Hl7.Fhir.Serialization.Shared.Tests/RoundtripAllSerializers.cs index ef60b48adf..09beaae327 100644 --- a/src/Hl7.Fhir.Serialization.Shared.Tests/RoundtripAllSerializers.cs +++ b/src/Hl7.Fhir.Serialization.Shared.Tests/RoundtripAllSerializers.cs @@ -1,10 +1,10 @@ #nullable enable +using Hl7.Fhir.ElementModel; using Hl7.Fhir.Model; using Hl7.Fhir.Specification; using Hl7.Fhir.Specification.Source; using Hl7.Fhir.Tests; -using Hl7.Fhir.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; @@ -14,250 +14,249 @@ using System.Reflection; -namespace Hl7.Fhir.Serialization.Tests +namespace Hl7.Fhir.Serialization.Tests; + +internal interface IRoundTripper { - internal interface IRoundTripper - { - string RoundTripXml(string original); - string RoundTripJson(string original); - } + string RoundTripXml(string original); + string RoundTripJson(string original); +} - internal class FhirSerializationEngineRoundtripper(IFhirSerializationEngine engine) : IRoundTripper - { - public string RoundTripXml(string original) => - engine.SerializeToXml( - engine.DeserializeFromJson( - engine.SerializeToJson( - (engine.DeserializeFromXml(original))!))!); - public string RoundTripJson(string original) => - engine.SerializeToJson( - (engine.DeserializeFromXml( - engine.SerializeToXml( - engine.DeserializeFromJson(original)!)))!); - } +internal class FhirSerializationEngineRoundtripper(IFhirSerializationEngine engine) : IRoundTripper +{ + public string RoundTripXml(string original) => + engine.SerializeToXml( + engine.DeserializeFromJson( + engine.SerializeToJson( + (engine.DeserializeFromXml(original))!))!); + public string RoundTripJson(string original) => + engine.SerializeToJson( + (engine.DeserializeFromXml( + engine.SerializeToXml( + engine.DeserializeFromJson(original)!)))!); +} - internal class TypedElementBasedRoundtripper(IStructureDefinitionSummaryProvider provider) : IRoundTripper +internal class TypedElementBasedRoundtripper(IStructureDefinitionSummaryProvider provider) : IRoundTripper +{ + public string RoundTripXml(string original) { - public string RoundTripXml(string original) - { - var nav = XmlParsingHelpers.ParseToTypedElement(original, provider, - new FhirXmlParsingSettings { PermissiveParsing = true }); - var json = nav.ToJson(); - var nav2 = JsonParsingHelpers.ParseToTypedElement(json, provider, - settings: new FhirJsonParsingSettings { AllowJsonComments = true, PermissiveParsing = true }); - return nav2.ToXml(); - } + var nav = XmlParsingHelpers.ParseToTypedElement(original, provider, + new FhirXmlParsingSettings { PermissiveParsing = true }); + var json = nav.ToJson(); + var nav2 = JsonParsingHelpers.ParseToTypedElement(json, provider, + settings: new FhirJsonParsingSettings { AllowJsonComments = true, PermissiveParsing = true }); + return nav2.ToXml(); + } - public string RoundTripJson(string original) - { - var nav = JsonParsingHelpers.ParseToTypedElement(original, provider, - settings: new FhirJsonParsingSettings { AllowJsonComments = true, PermissiveParsing = true }); - var xml = nav.ToXml(); - var nav2 = XmlParsingHelpers.ParseToTypedElement(xml, provider, - new FhirXmlParsingSettings { PermissiveParsing = true }); - return nav2.ToJson(); - } + public string RoundTripJson(string original) + { + var nav = JsonParsingHelpers.ParseToTypedElement(original, provider, + settings: new FhirJsonParsingSettings { AllowJsonComments = true, PermissiveParsing = true }); + var xml = nav.ToXml(); + var nav2 = XmlParsingHelpers.ParseToTypedElement(xml, provider, + new FhirXmlParsingSettings { PermissiveParsing = true }); + return nav2.ToJson(); } +} - [TestClass] - public class RoundtripAllSerializers - { - private static readonly IFhirSerializationEngine NEW_POCO_ENGINE = - FhirSerializationEngineFactory.Ostrich(ModelInfo.ModelInspector); +[TestClass] +public class RoundtripAllSerializers +{ + private static readonly IFhirSerializationEngine NEW_POCO_ENGINE = + FhirSerializationEngineFactory.Ostrich(ModelInfo.ModelInspector); - private static readonly IRoundTripper NEW_POCO_ROUNDTRIPPER = - new FhirSerializationEngineRoundtripper(NEW_POCO_ENGINE); + private static readonly IRoundTripper NEW_POCO_ROUNDTRIPPER = + new FhirSerializationEngineRoundtripper(NEW_POCO_ENGINE); - private static readonly IRoundTripper OLD_POCO_ROUNDTRIPPER = - new FhirSerializationEngineRoundtripper( - FhirSerializationEngineFactory.Legacy.Ostrich(ModelInfo.ModelInspector)); + private static readonly IRoundTripper OLD_POCO_ROUNDTRIPPER = + new FhirSerializationEngineRoundtripper( + FhirSerializationEngineFactory.Legacy.Ostrich(ModelInfo.ModelInspector)); - private static readonly IRoundTripper TYPEDELEM_POCOSDPROV = - new TypedElementBasedRoundtripper(new PocoStructureDefinitionSummaryProvider()); + private static readonly IRoundTripper TYPEDELEM_POCOSDPROV = + new TypedElementBasedRoundtripper(new PocoStructureDefinitionSummaryProvider()); - private static readonly IResourceResolver SOURCE = new CachedResolver(ZipSource.CreateValidationSource()); + private static readonly IResourceResolver SOURCE = new CachedResolver(ZipSource.CreateValidationSource()); - private static readonly IRoundTripper TYPEDELEM_SDPROV = - new TypedElementBasedRoundtripper(new StructureDefinitionSummaryProvider(SOURCE)); + private static readonly IRoundTripper TYPEDELEM_SDPROV = + new TypedElementBasedRoundtripper(new StructureDefinitionSummaryProvider(SOURCE)); - private const string XML_EXAMPLE_ZIP_NAME = "examples.zip"; - private const string JSON_EXAMPLE_ZIP_NAME = "examples-json.zip"; + private const string XML_EXAMPLE_ZIP_NAME = "examples.zip"; + private const string JSON_EXAMPLE_ZIP_NAME = "examples-json.zip"; - [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, - DynamicDataDisplayName = nameof(GetTestDisplayNames))] - [DataTestMethod] - [TestCategory("LongRunner")] - public void FullRoundtripOfAllExamplesNewPocoSerializer(ZipArchiveEntry entry) - { - doRoundTrip(entry, NEW_POCO_ROUNDTRIPPER); - } + [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, + DynamicDataDisplayName = nameof(GetTestDisplayNames))] + [DataTestMethod] + [TestCategory("LongRunner")] + public void FullRoundtripOfAllExamplesNewPocoSerializer(ZipArchiveEntry entry) + { + doRoundTrip(entry, NEW_POCO_ROUNDTRIPPER); + } - [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, - DynamicDataDisplayName = nameof(GetTestDisplayNames))] - [DataTestMethod] - [TestCategory("LongRunner")] - public void FullRoundtripOfAllExamplesOldPocoSerializer(ZipArchiveEntry entry) - { - doRoundTrip(entry, OLD_POCO_ROUNDTRIPPER); - } + [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, + DynamicDataDisplayName = nameof(GetTestDisplayNames))] + [DataTestMethod] + [TestCategory("LongRunner")] + public void FullRoundtripOfAllExamplesOldPocoSerializer(ZipArchiveEntry entry) + { + doRoundTrip(entry, OLD_POCO_ROUNDTRIPPER); + } - [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, - DynamicDataDisplayName = nameof(GetTestDisplayNames))] - [DataTestMethod] - [TestCategory("LongRunner")] - public void FullRoundtripOfAllExamplesPocoSdProvTypedElementSerializer(ZipArchiveEntry entry) - { - doRoundTrip(entry, TYPEDELEM_POCOSDPROV); - } + [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, + DynamicDataDisplayName = nameof(GetTestDisplayNames))] + [DataTestMethod] + [TestCategory("LongRunner")] + public void FullRoundtripOfAllExamplesPocoSdProvTypedElementSerializer(ZipArchiveEntry entry) + { + doRoundTrip(entry, TYPEDELEM_POCOSDPROV); + } - [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, - DynamicDataDisplayName = nameof(GetTestDisplayNames))] - [DataTestMethod] - [TestCategory("LongRunner")] - public void FullRoundtripOfAllExamplesSdProvTypedElementSerializer(ZipArchiveEntry entry) - { - doRoundTrip(entry, TYPEDELEM_SDPROV); - } + [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, + DynamicDataDisplayName = nameof(GetTestDisplayNames))] + [DataTestMethod] + [TestCategory("LongRunner")] + public void FullRoundtripOfAllExamplesSdProvTypedElementSerializer(ZipArchiveEntry entry) + { + doRoundTrip(entry, TYPEDELEM_SDPROV); + } - private static IEnumerable prepareExampleZipFiles() - { - var xmlExampleArchive = openTestZip(XML_EXAMPLE_ZIP_NAME); - var jsonExampleArchive = openTestZip(JSON_EXAMPLE_ZIP_NAME); + private static IEnumerable prepareExampleZipFiles() + { + var xmlExampleArchive = openTestZip(XML_EXAMPLE_ZIP_NAME); + var jsonExampleArchive = openTestZip(JSON_EXAMPLE_ZIP_NAME); - var relevantEntries = xmlExampleArchive.Entries.Concat(jsonExampleArchive.Entries) - .Where(e => e.Name.EndsWith(".xml") || e.Name.EndsWith(".json")) - .Where(f => !skipFile(f.Name)) - .ToList(); + var relevantEntries = xmlExampleArchive.Entries.Concat(jsonExampleArchive.Entries) + .Where(e => e.Name.EndsWith(".xml") || e.Name.EndsWith(".json")) + .Where(f => !skipFile(f.Name)) + .ToList(); - return relevantEntries.Select(z => (object[]) [z]) - .ToList(); - } + return relevantEntries.Select(z => (object[]) [z]) + .ToList(); + } - private static bool skipFile(string file) - { - if (file.Contains("notification-") || file.Contains("subscriptionstatus-")) - return true; // These are Subscription resources that have invalid data in R5. - if (file.Contains("examplescenario-example")) - return - true; // this resource has a property name resourceType (which is reserved in the .net json serializer) - if (file.Contains("json-edge-cases")) - return true; // known issues with binary contained resource having content, not data - if (file.Contains("observation-decimal")) - return true; // exponential number example is tooo big (and too small) - if (file.Contains("package-min-ver")) - return true; // not a resource - if (file.Contains("profiles-other")) - return true; - if (file.Contains("profiles-resources")) - return true; - if (file.Contains("profiles-types")) - return true; - if (file.Contains("dataelements")) - return true; - if (file.Contains("valuesets")) - return true; - // https://chat.fhir.org/#narrow/stream/48-terminology/subject/v2.20Table.20 - if (file.Contains("xver-paths-4.6") || file.Contains("hl7.fhir.r5.corexml.manifest") || - file.Contains("hl7.fhir.r5.expansions.manifest") || file.Contains("hl7.fhir.r5.core.manifest") || - file.Contains("uml")) - return true; // non-fhir-files in the R5 examples.zip + private static bool skipFile(string file) + { + if (file.Contains("notification-") || file.Contains("subscriptionstatus-")) + return true; // These are Subscription resources that have invalid data in R5. + if (file.Contains("examplescenario-example")) + return + true; // this resource has a property name resourceType (which is reserved in the .net json serializer) + if (file.Contains("json-edge-cases")) + return true; // known issues with binary contained resource having content, not data + if (file.Contains("observation-decimal")) + return true; // exponential number example is tooo big (and too small) + if (file.Contains("package-min-ver")) + return true; // not a resource + if (file.Contains("profiles-other")) + return true; + if (file.Contains("profiles-resources")) + return true; + if (file.Contains("profiles-types")) + return true; + if (file.Contains("dataelements")) + return true; + if (file.Contains("valuesets")) + return true; + // https://chat.fhir.org/#narrow/stream/48-terminology/subject/v2.20Table.20 + if (file.Contains("xver-paths-4.6") || file.Contains("hl7.fhir.r5.corexml.manifest") || + file.Contains("hl7.fhir.r5.expansions.manifest") || file.Contains("hl7.fhir.r5.core.manifest") || + file.Contains("uml")) + return true; // non-fhir-files in the R5 examples.zip #if R4 - if (file.Contains("v2-tables")) - return true; // this file is known to have a single dud valueset - have reported on Zulip + if (file.Contains("v2-tables")) + return true; // this file is known to have a single dud valueset - have reported on Zulip #endif #if R5 // These examples contain resourceType which cannot be handled by our serializers if (file.Contains("subscription-example")) return true; if (file.Contains("consent-example-smartonfhir")) return true; #endif - return false; - } + return false; + } - public static string GetTestDisplayNames(MethodInfo _, object[] values) => - ((ZipArchiveEntry)values[0]).Name; + public static string GetTestDisplayNames(MethodInfo _, object[] values) => + ((ZipArchiveEntry)values[0]).Name; - private static void doRoundTrip(ZipArchiveEntry entry, IRoundTripper roundtripper) - { - var errors = new List(); + private static void doRoundTrip(ZipArchiveEntry entry, IRoundTripper roundtripper) + { + var errors = new List(); - using var dataStream = entry.Open(); - var streamReader = new StreamReader(dataStream); - var input = streamReader.ReadToEnd(); - var name = entry.Name; + using var dataStream = entry.Open(); + var streamReader = new StreamReader(dataStream); + var input = streamReader.ReadToEnd(); + var name = entry.Name; - var result = roundtripFile(input, name, roundtripper, errors); - if(result is not null) - compare(input, result, name, errors); + var result = roundtripFile(input, name, roundtripper, errors); + if(result is not null) + compare(input, result, name, errors); - Assert.AreEqual(0, errors.Count, "Errors were encountered comparing converted content"); - } + Assert.AreEqual(0, errors.Count, "Errors were encountered comparing converted content"); + } + + private static ZipArchive openTestZip(string filename) + { + var fullPath = Path.Combine("TestData", filename); + return ZipFile.OpenRead(fullPath); + } - private static ZipArchive openTestZip(string filename) + private static string? roundtripFile(string input, string name, IRoundTripper roundtripper, ICollection errors) + { + var isXml = name.EndsWith(".xml"); + + try { - var fullPath = Path.Combine("TestData", filename); - return ZipFile.OpenRead(fullPath); + return isXml ? + roundtripper.RoundTripXml(input) : + roundtripper.RoundTripJson(input); } - - private static string? roundtripFile(string input, string name, IRoundTripper roundtripper, ICollection errors) + catch (Exception ex) { - var isXml = name.EndsWith(".xml"); - - try - { - return isXml ? - roundtripper.RoundTripXml(input) : - roundtripper.RoundTripJson(input); - } - catch (Exception ex) - { - errors.Add(ex.Message); - return null; - } + errors.Add(ex.Message); + return null; } + } - private static void compare(string expectedData, string actualData, string name, List errors) + private static void compare(string expectedData, string actualData, string name, List errors) + { + if (name.EndsWith(".xml")) { - if (name.EndsWith(".xml")) + try { - try - { - XmlAssert.AreSame(name, expectedData, actualData); - } - catch (Exception e) - { - errors.Add(e.Message); - } + XmlAssert.AreSame(name, expectedData, actualData); } - else + catch (Exception e) { - JsonAssert.AreSame(name, expectedData, actualData, errors); + errors.Add(e.Message); } } - - [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, - DynamicDataDisplayName = nameof(GetTestDisplayNames))] - [DataTestMethod] - [TestCategory("LongRunner")] - public void TestMatchAndExactly(ZipArchiveEntry entry) + else { - using var dataStream = entry.Open(); - var streamReader = new StreamReader(dataStream); - var input = streamReader.ReadToEnd(); - var name = entry.Name; + JsonAssert.AreSame(name, expectedData, actualData, errors); + } + } + + [DynamicData(nameof(prepareExampleZipFiles), DynamicDataSourceType.Method, + DynamicDataDisplayName = nameof(GetTestDisplayNames))] + [DataTestMethod] + [TestCategory("LongRunner")] + public void TestMatchAndExactly(ZipArchiveEntry entry) + { + using var dataStream = entry.Open(); + var streamReader = new StreamReader(dataStream); + var input = streamReader.ReadToEnd(); + var name = entry.Name; - var resource = name.EndsWith(".xml") - ? NEW_POCO_ENGINE.DeserializeFromXml(input) - : NEW_POCO_ENGINE.DeserializeFromJson(input); + var resource = name.EndsWith(".xml") + ? NEW_POCO_ENGINE.DeserializeFromXml(input) + : NEW_POCO_ENGINE.DeserializeFromJson(input); - var r2 = (Resource)resource!.DeepCopy(); - Assert.IsTrue(resource.Matches(r2), - "Serialization of " + name + " did not match output - Matches test"); - Assert.IsTrue(resource.IsExactly(r2), - "Serialization of " + name + " did not match output - IsExactly test"); - //EK 2024-12-05: Removed this test as I think the logic of matches is that anything matches the null pattern. - //Assert.IsFalse(resource.Matches(null), "Serialization of " + name + " matched null - Matches test"); - Assert.IsFalse(resource.IsExactly(null), - "Serialization of " + name + " matched null - IsExactly test"); - } + var r2 = resource!.DeepCopy(); + Assert.IsTrue(resource.Matches(r2), + "Serialization of " + name + " did not match output - Matches test"); + Assert.IsTrue(resource.IsExactly(r2), + "Serialization of " + name + " did not match output - IsExactly test"); + //EK 2024-12-05: Removed this test as I think the logic of matches is that anything matches the null pattern. + //Assert.IsFalse(resource.Matches(null), "Serialization of " + name + " matched null - Matches test"); + Assert.IsFalse(resource.IsExactly(null), + "Serialization of " + name + " matched null - IsExactly test"); } } \ No newline at end of file diff --git a/src/Hl7.Fhir.Serialization.Shared.Tests/SerializePartialTree.cs b/src/Hl7.Fhir.Serialization.Shared.Tests/SerializePartialTree.cs index 1c4900f6ca..4c9e54ac52 100644 --- a/src/Hl7.Fhir.Serialization.Shared.Tests/SerializePartialTree.cs +++ b/src/Hl7.Fhir.Serialization.Shared.Tests/SerializePartialTree.cs @@ -32,7 +32,6 @@ public void DeterminePocoInstanceTypeWithRedirect() } [TestMethod] - [TemporarilyChanged] // We once again cannot do this, as base no longer knows if it has a parent public async Tasks.Task CanSerializeSubtree() { var tpXml = await File.ReadAllTextAsync(Path.Combine("TestData", "fp-test-patient.xml")); diff --git a/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.json b/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.json index 69dbce9c22..732a27bb53 100644 --- a/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.json +++ b/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.json @@ -33,8 +33,7 @@ ], "code": "_text", "type": "string", - "description": "Search on the narrative of the resource", - "xpathUsage": "normal" + "description": "Search on the narrative of the resource" } } ] diff --git a/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.xml b/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.xml index 0fedceb95b..3cfa8a213c 100644 --- a/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.xml +++ b/src/Hl7.Fhir.Serialization.Shared.Tests/TestData/BundleWithOneEntry.xml @@ -27,10 +27,7 @@ - - - diff --git a/src/Hl7.Fhir.Shared.Tests/Validation/SearchDataExtraction.cs b/src/Hl7.Fhir.Shared.Tests/Validation/SearchDataExtraction.cs index 3ead98b46d..538a0fcd3e 100644 --- a/src/Hl7.Fhir.Shared.Tests/Validation/SearchDataExtraction.cs +++ b/src/Hl7.Fhir.Shared.Tests/Validation/SearchDataExtraction.cs @@ -175,7 +175,7 @@ private static void ExtractExamplesFromResource(Dictionary exampleS } } - private static IScopedNode mockResolver(string url) + private static PocoNode mockResolver(string url) { ResourceIdentity ri = new ResourceIdentity(url); if (!string.IsNullOrEmpty(ri.ResourceType)) diff --git a/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/PocoBuilderExtensions.cs b/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/PocoBuilderExtensions.cs deleted file mode 100644 index 2c5cd49715..0000000000 --- a/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/PocoBuilderExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018, 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://github.com/FirelyTeam/firely-net-sdk/blob/master/LICENSE - */ - - -using Hl7.Fhir.Model; -using Hl7.Fhir.Serialization; -using System; - -namespace Hl7.Fhir.ElementModel -{ - public static class PocoBuilderExtensions - { - public static Base ToPoco(this ISourceNode source, Type pocoType = null, PocoBuilderSettings settings = null) => - source.ToPoco(ModelInfo.ModelInspector, pocoType, settings); - - public static T ToPoco(this ISourceNode source, PocoBuilderSettings settings = null) where T : Base => - (T)source.ToPoco(ModelInfo.ModelInspector, typeof(T), settings); - - public static Base ToPoco(this ITypedElement element, PocoBuilderSettings settings = null) => - element.ToPoco(ModelInfo.ModelInspector, settings); - - public static T ToPoco(this ITypedElement element, PocoBuilderSettings settings = null) where T : Base => - (T)element.ToPoco(ModelInfo.ModelInspector, settings); - } -} diff --git a/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/TypedElementExtensions.cs b/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/TypedElementExtensions.cs deleted file mode 100644 index 32a25810b3..0000000000 --- a/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/TypedElementExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2016, 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.Model; -using Hl7.Fhir.Utility; -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Hl7.Fhir.ElementModel -{ - public static class ShimsTypedElementExtensions - { - /// - /// Creates an adapter which implements ITypedElement on top of a POCO instance, using the legacy ElementNode stack. - /// - /// The POCO instance - /// The name you wish to have at the root of the tree. This will determine e.g. the root element name for serialization. - /// If none is given, the type of the underlying poco will be used. - public static ITypedElement ToTypedElementLegacy(this Base @base, string? rootName = null) - => @base.ToTypedElementLegacy(ModelInfo.ModelInspector, rootName); - - /// - /// Creates an adapter which implements ITypedElement on top of a POCO instance, with explicit version-specific metadata. - /// - /// The POCO instance - /// The name you wish to have at the root of the tree. This will determine e.g. the root element name for serialization. - /// If none is given, the type of the underlying poco will be used. - /// The implementation of this method has changed. If you notice regressions, please let the SDK team know. - /// In the meantime, you can restore the old behaviour with a call to -#if NETSTANDARD2_1 - [Obsolete("The implementation of this method has changed to use our new model stack. If you want to try the new behaviour, "+ - "either ignore this warning or call ToPocoNode(). For reverting to the old behaviour, call .ToTypedElementLegacy()")] -#else - [Experimental("SDK0001")] -#endif - public static ITypedElement ToTypedElement(this Base @base, string? rootName = null) => - @base.ToTypedElement(ModelInfo.ModelInspector, rootName); - } -} -#nullable restore \ No newline at end of file diff --git a/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/VersionedConversionExtensions.cs b/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/VersionedConversionExtensions.cs new file mode 100644 index 0000000000..941e347839 --- /dev/null +++ b/src/Hl7.Fhir.Shims.STU3AndUp/ElementModel/VersionedConversionExtensions.cs @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016, 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.Model; +using Hl7.Fhir.Serialization; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Hl7.Fhir.ElementModel; + +/// +/// A collection of all extension methods which require version-specific metadata included in the assembly they are compiled into. +/// +public static class VersionedConversionExtensions +{ + /// + /// Creates an adapter which implements ITypedElement on top of a POCO instance, using the legacy ElementNode stack. + /// + /// The POCO instance + /// The name you wish to have at the root of the tree. This will determine e.g. the root element name for serialization. + /// If none is given, the type of the underlying poco will be used. + public static ITypedElement ToTypedElementLegacy(this Base @base, string? rootName = null) + => @base.ToTypedElementLegacy(ModelInfo.ModelInspector, rootName); + + /// + /// Creates an adapter which implements ITypedElement on top of a POCO instance, with explicit version-specific metadata. + /// + /// The POCO instance + /// The name you wish to have at the root of the tree. This will determine e.g. the root element name for serialization. + /// If none is given, the type of the underlying poco will be used. + /// The implementation of this method has changed. If you notice regressions, please let the SDK team know. + /// In the meantime, you can restore the old behaviour with a call to +#if NETSTANDARD2_1 + [Obsolete("The implementation of this method has changed to use our new model stack. If you want to try the new behaviour, "+ + "either ignore this warning or call ToPocoNode(). For reverting to the old behaviour, call .ToTypedElementLegacy()")] +#else + [Experimental("SDK0001")] +#endif + public static ITypedElement ToTypedElement(this Base @base, string? rootName = null) => + @base.ToTypedElement(ModelInfo.ModelInspector, rootName); + + public static Base ToPoco(this ISourceNode source, Type? pocoType = null, PocoBuilderSettings? settings = null) => + source.ToPoco(ModelInfo.ModelInspector, pocoType, settings); + + public static T ToPoco(this ISourceNode source, PocoBuilderSettings? settings = null) where T : Base => + (T)source.ToPoco(ModelInfo.ModelInspector, typeof(T), settings); + + public static Base ToPoco(this ITypedElement element, PocoBuilderSettings? settings = null) => + element.ToPoco(ModelInfo.ModelInspector, settings); + + public static T ToPoco(this ITypedElement element, PocoBuilderSettings? settings = null) where T : Base => + (T)element.ToPoco(ModelInfo.ModelInspector, settings); +} \ No newline at end of file diff --git a/src/Hl7.Fhir.Shims.STU3AndUp/FhirPath/FhirPathExtensions.cs b/src/Hl7.Fhir.Shims.STU3AndUp/FhirPath/FhirPathExtensions.cs index a53b73a2ee..aadfc82968 100644 --- a/src/Hl7.Fhir.Shims.STU3AndUp/FhirPath/FhirPathExtensions.cs +++ b/src/Hl7.Fhir.Shims.STU3AndUp/FhirPath/FhirPathExtensions.cs @@ -22,21 +22,6 @@ public static class FhirPathExtensions private static readonly FhirPathCompiler COMPILER = new(new SymbolTable().AddStandardFP().AddFhirExtensions()); private static readonly FhirPathCompilerCache CACHE = new(COMPILER); - /// - /// Converts results of a resolver from Resource to IScopedNode - /// - /// results of a resolver as Resource - /// Result of the convertion to IScopedNode - public static Func ToFhirPathResolver(this Func resolver) - { - return navResolver; - - IScopedNode? navResolver(string url) - { - return resolver(url); - } - } - /// /// Expose the SymbolTable of the compiler, so we can add extra symbols to it. /// @@ -45,23 +30,23 @@ public static class FhirPathExtensions /// now used by 1 unit test FhirPathScaleTest internal static SymbolTable GetSymbols() => COMPILER.Symbols; - /// + /// public static IEnumerable Select(this Base input, string expression, FhirEvaluationContext? ctx = null) => CACHE.Select(input.ToPocoNode(), expression, ctx ?? new FhirEvaluationContext()).ToFhirValues(); - /// + /// public static object? Scalar(this Base input, string expression, FhirEvaluationContext? ctx = null) => CACHE.Scalar(input.ToPocoNode(), expression, ctx ?? new FhirEvaluationContext()); - /// + /// public static bool Predicate(this Base input, string expression, FhirEvaluationContext? ctx = null) => CACHE.Predicate(input.ToPocoNode(), expression, ctx ?? new FhirEvaluationContext()); - /// + /// public static bool IsTrue(this Base input, string expression, FhirEvaluationContext? ctx = null) => CACHE.IsTrue(input.ToPocoNode(), expression, ctx ?? new FhirEvaluationContext()); - /// + /// public static bool IsBoolean(this Base input, string expression, bool value, FhirEvaluationContext? ctx = null) => CACHE.IsBoolean(input.ToPocoNode(), expression, value, ctx ?? new FhirEvaluationContext()); } diff --git a/src/Hl7.Fhir.Shims.STU3AndUp/Hl7.Fhir.Shims.STU3AndUp.projitems b/src/Hl7.Fhir.Shims.STU3AndUp/Hl7.Fhir.Shims.STU3AndUp.projitems index f7d9ce4a80..4665b477a0 100644 --- a/src/Hl7.Fhir.Shims.STU3AndUp/Hl7.Fhir.Shims.STU3AndUp.projitems +++ b/src/Hl7.Fhir.Shims.STU3AndUp/Hl7.Fhir.Shims.STU3AndUp.projitems @@ -9,8 +9,7 @@ Hl7.Fhir.Shims.STU3AndUp - - + diff --git a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorManifestTests.cs b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorManifestTests.cs index 30992d799f..26c1ee9c80 100644 --- a/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorManifestTests.cs +++ b/src/Hl7.Fhir.Specification.Shared.Tests/Snapshot/SnapshotGeneratorManifestTests.cs @@ -772,7 +772,7 @@ void ReplaceTestRule(string id, string originalExpression, string fixedExpressio // Custom context for accessing input & expected result class SnapshotEvaluationContext : FhirEvaluationContext { - Dictionary _aliases; + Dictionary _aliases; string _testPath; public SnapshotEvaluationContext( @@ -792,12 +792,12 @@ public SnapshotEvaluationContext( this.WithResourceOverrides(Generated); } - void Trace(string msg, IEnumerable elems) + void Trace(string msg, IEnumerable elems) { Console.WriteLine($"[TRACE] {msg}:"); foreach (var elem in elems) { - Console.WriteLine($"[TRACE] '{elem.Name}' : {elem.InstanceType}{(elem.HasValue() ? $" = '{elem.Value.ToString()}'" : null)}"); + Console.WriteLine($"[TRACE] '{elem.Name}' : {elem.Poco.TypeName}{(elem.HasValue() ? $" = '{elem.GetValue()!}'" : null)}"); } } @@ -805,19 +805,19 @@ void Trace(string msg, IEnumerable elems) public IResourceResolver TestResolver { get; } - public IScopedNode Input { get; } + public PocoNode Input { get; } - public IScopedNode Generated { get; } + public PocoNode Generated { get; } // Custom FhirPath method implementations - Dictionary Aliases => _aliases ?? (_aliases = new Dictionary()); + Dictionary Aliases => _aliases ?? (_aliases = new Dictionary()); - void AddAlias(string alias, IScopedNode elem) => Aliases[alias] = elem; + void AddAlias(string alias, PocoNode elem) => Aliases[alias] = elem; - IScopedNode Alias(string alias) => Aliases[alias]; + PocoNode Alias(string alias) => Aliases[alias]; - IScopedNode Fixture(string name) + PocoNode Fixture(string name) { if (name == $"{Id}-input") { return Input; } if (name == $"{Id}-output") { return Generated; } @@ -851,18 +851,18 @@ IScopedNode Fixture(string name) // Add custom FHIRPath methods for unit testing public static void AddSymbols(SymbolTable symbols) { - symbols.Add("fixture", Fixture); - symbols.Add("aliasAs", AliasAs); - symbols.Add("alias", Alias); - symbols.Add("check", Check); + symbols.Add("fixture", Fixture); + symbols.Add("aliasAs", AliasAs); + symbols.Add("alias", Alias); + symbols.Add("check", Check); } // Custom FHIRPath methods for unit testing - public static IScopedNode Fixture(IScopedNode elem, string name, EvaluationContext ctx) + public static PocoNode Fixture(PocoNode elem, string name, EvaluationContext ctx) => ctx is SnapshotEvaluationContext sctx ? sctx.Fixture(name) : null; - public static IScopedNode AliasAs(IScopedNode elem, string id, EvaluationContext ctx) + public static PocoNode AliasAs(PocoNode elem, string id, EvaluationContext ctx) { if (ctx is SnapshotEvaluationContext sctx) { @@ -871,10 +871,10 @@ public static IScopedNode AliasAs(IScopedNode elem, string id, EvaluationContext return elem; } - public static IScopedNode Alias(IScopedNode elem, string id, EvaluationContext ctx) + public static PocoNode Alias(PocoNode elem, string id, EvaluationContext ctx) => ctx is SnapshotEvaluationContext sctx ? sctx.Alias(id) : null; - public static IScopedNode Check(IScopedNode elem, bool condition, string message, EvaluationContext ctx) + public static PocoNode Check(PocoNode elem, bool condition, string message, EvaluationContext ctx) { Assert.IsTrue(condition, $"[CHECK] '{elem.Name}' {message}"); //if (!condition) diff --git a/src/Hl7.Fhir.Support.Tests/ElementModel/TypedElementExtensionTests.cs b/src/Hl7.Fhir.Support.Tests/ElementModel/TypedElementExtensionTests.cs index 3e4a1096bb..58db1f513d 100644 --- a/src/Hl7.Fhir.Support.Tests/ElementModel/TypedElementExtensionTests.cs +++ b/src/Hl7.Fhir.Support.Tests/ElementModel/TypedElementExtensionTests.cs @@ -7,7 +7,7 @@ namespace Hl7.Fhir.ElementModel { [TestClass] - public class TypedElementExtensionsTests + public class VersionedConversionExtensionsTests { private readonly IStructureDefinitionSummaryProvider _provider = new NoTypeProvider(); diff --git a/src/Hl7.Fhir.Support.Tests/FhirPath/FhirPathTests.cs b/src/Hl7.Fhir.Support.Tests/FhirPath/FhirPathTests.cs index b6bf098b4c..978f94a433 100644 --- a/src/Hl7.Fhir.Support.Tests/FhirPath/FhirPathTests.cs +++ b/src/Hl7.Fhir.Support.Tests/FhirPath/FhirPathTests.cs @@ -56,7 +56,7 @@ public void ResolveOnEmptyTest() public void HtmlChecks(string xml, bool expected, string because) { var evaluator = _compiler.Compile("htmlChecks()"); - evaluator.Predicate(PocoNodeOrList.ForPrimitive(xml), new FhirEvaluationContext()).Should().Be(expected, because); + evaluator.Predicate(PocoNode.ForPrimitive(xml), new FhirEvaluationContext()).Should().Be(expected, because); } [DataTestMethod] @@ -79,7 +79,6 @@ public static IEnumerable GetTypedElements() var poco = sourceNode.ToPoco(ModelInspector.Base); yield return new object[] { poco.ToTypedElement(ModelInspector.Base), "poco to TypedElement" }; - } public static IEnumerable LowBoundaryTestCases() => @@ -150,11 +149,11 @@ public static IEnumerable AllTestCases() => public void AssertFhirPathTestcases(string expression, bool expected) { var evaluator = _compiler.Compile(expression); - var result = evaluator(null, new FhirEvaluationContext()); + var result = evaluator(PocoNode.ForPrimitive(true), new FhirEvaluationContext()); if (result.Any()) { - result.Should().ContainSingle().Which.Value.Should().Be(expected); + result.Should().ContainSingle().Which.GetValue().Should().Be(expected); } else { diff --git a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathContextTests.cs b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathContextTests.cs index bfd43cf23b..54cd826119 100644 --- a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathContextTests.cs +++ b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathContextTests.cs @@ -12,14 +12,14 @@ namespace Hl7.FhirPath.R4.Tests.PocoTests; [TestClass] public class FhirPathContextTests { - IScopedNode _bundle; + PocoNode _bundle; [TestInitialize] public void SetupSource() { var bundleXml = File.ReadAllText(Path.Combine("TestData", "bundle-contained-references.xml")); - _bundle = ((new FhirXmlParser()).Parse(bundleXml)).ToTypedElement().ToScopedNode(); + _bundle = (new FhirXmlParser()).Parse(bundleXml).ToPocoNode(); } [TestMethod] @@ -28,11 +28,11 @@ public void TestFhirEvaluationContext() _bundle.IsTrue("entry[2].resource.contained[0].select(%resource) = %resource"); // should stay the same _bundle.IsTrue("%rootResource = Bundle.entry[2].resource.contained[0].select(%rootResource)"); // should stay the same - var elemInContainedResource = _bundle.Children("entry").Skip(2).First().Children("resource").First().Children("contained").First().Children("id").First().ToScopedNode(); + var elemInContainedResource = _bundle.Child("entry").Skip(2).First().Child("resource").First().Child("contained").First().Child("id").First(); elemInContainedResource.IsTrue("%rootResource != %resource"); // should be true elemInContainedResource.Select("%rootResource").Should().BeEquivalentTo(_bundle.Select("entry[2].resource")); - var elemInDomainResource = _bundle.Children("entry").Skip(2).First().Children("resource").First().Children("id").First().ToScopedNode(); + var elemInDomainResource = _bundle.Child("entry").Skip(2).First().Child("resource").First().Child("id").First().First(); elemInDomainResource.IsTrue("%rootResource = %resource"); elemInDomainResource.Select("%rootResource").Should().BeEquivalentTo(_bundle.Select("entry[2].resource")); } diff --git a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathEvaluatorTest.cs b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathEvaluatorTest.cs index 4f08ef6409..3e813fe337 100644 --- a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathEvaluatorTest.cs +++ b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathEvaluatorTest.cs @@ -282,19 +282,19 @@ public void TestExpression() fixture.IsTrue(@"(Patient.identifier.where( use = ( 'offic' + 'ial')) = Patient.identifier.skip(8 div 2 - 3*2 + 3)) and (Patient.identifier.where(use='usual') = Patient.identifier.first())"); - + fixture.IsTrue(@"(1|2|3|4|5).where($this > 2 and $this <= 4) = (3|4)"); - + fixture.IsTrue(@"(1|2|2|3|Patient.identifier.first()|Patient.identifier).distinct().count() = 3 + Patient.identifier.count()"); - + fixture.IsTrue(@"(Patient.identifier.where(use='official').last() in Patient.identifier) and (Patient.identifier.first() in Patient.identifier.tail()).not()"); - + fixture.IsTrue(@"Patient.identifier.any(use='official') and identifier.where(use='usual').exists()"); - + fixture.IsTrue(@"Patient.descendants().where($this.as(string).contains('222'))[1] = %context.contained.address.line"); - + fixture.IsTrue(@"Patient.name.select(given|family).count() = 2"); fixture.IsTrue(@"Patient.identifier.where(use = 'official').select(value + 'yep') = ('7654321yep' | '11223344yep')"); fixture.IsTrue(@"Patient.descendants().where(($this is code) and ($this.contains('wne'))).trace('them') = contact.relationship.coding.code"); @@ -567,7 +567,6 @@ public void sequence_of_variable_definitions_tweak() { var expr = "Patient.name.defineVariable('n2', skip(1).first()).defineVariable('res', %n2.given+%n2.given).select(%res)"; var r = fixture.PatientExample.Select(expr).ToList(); - foreach (var item in r) { Console.WriteLine(item.ToXml()); } Assert.AreEqual(2, r.Count()); Assert.AreEqual("JimJim", r.First().ToString()); Assert.AreEqual("JimJim", r.Skip(1).First().ToString()); @@ -607,8 +606,8 @@ public void defineVariable_with_compile_success() var exprCompiled = compiler.Compile(expr); var r = exprCompiled(fixture.PatientExample.ToPocoNode(), new FhirEvaluationContext()); Assert.AreEqual(2, r.Count()); - Assert.AreEqual("r1-v1", r.First().Value); - Assert.AreEqual("r1-v2", r.Skip(1).First().Value); + Assert.AreEqual("r1-v1", r.First().GetValue()); + Assert.AreEqual("r1-v2", r.Skip(1).First().GetValue()); // .toStrictEqual(["r1-v1", "r1-v2"]); } /* diff --git a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathExtensionsTest.cs b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathExtensionsTest.cs index e206923e27..6db0c2fd55 100644 --- a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathExtensionsTest.cs +++ b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathExtensionsTest.cs @@ -55,7 +55,7 @@ public void TestResolve2() var result = _bundle.Select(statement, new FhirEvaluationContext() { ElementResolver = resolver }); Assert.IsTrue(called); - IScopedNode resolver(string url) + PocoNode resolver(string url) { called = true; return null; diff --git a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathNavTest.cs b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathNavTest.cs index f33a463760..d30f76cefe 100644 --- a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathNavTest.cs +++ b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathNavTest.cs @@ -24,7 +24,7 @@ namespace Hl7.FhirPath.R4.Tests { public class FhirPathNavTest { - public IScopedNode getTestData() + public PocoNode getTestData() { var tpXml = TestData.ReadTextFile("fp-test-patient.xml"); var engine = FhirSerializationEngineFactory.Ostrich(ModelInfo.ModelInspector); @@ -40,7 +40,7 @@ public void TestNavigation() var result = values.Navigate("Patient").Navigate("identifier").Navigate("use"); Assert.Equal(3, result.Count()); - Assert.Equal("usual", result.First().Value); + Assert.Equal("usual", result.First().GetValue()); } [Fact] @@ -50,7 +50,7 @@ public void TestNavigationALTERNATIVE() var result = values.Navigate("Patient").Navigate("identifier").Navigate("use"); Assert.Equal(3, result.Count()); - Assert.Equal("usual", (string)result.First().Value); + Assert.Equal("usual", (string)result.First().GetValue()); } } diff --git a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathParallelTest.cs b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathParallelTest.cs index d90f344cd4..6e1c52ebf2 100644 --- a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathParallelTest.cs +++ b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathParallelTest.cs @@ -34,8 +34,8 @@ public static void Initialize(TestContext ctx) [TestCategory("LongRunner")] public async Tasks.Task TestSelectMethods() { - await MassiveParallelSelectsShouldBeCorrect("Api", new Func>((nav, expr, context) => IValueProviderFPExtensions.Select(nav, expr, context))); - await MassiveParallelSelectsShouldBeCorrect("Concurrent", new Func>((nav, expr, context) => FhirPathExtensions.Select(nav, expr, context))); + await MassiveParallelSelectsShouldBeCorrect("Api", new Func>((nav, expr, context) => IValueProviderFPExtensions.Select(nav, expr, context))); + await MassiveParallelSelectsShouldBeCorrect("Concurrent", new Func>((nav, expr, context) => FhirPathExtensions.Select(nav, expr, context))); } /// @@ -46,7 +46,7 @@ public async Tasks.Task TestSelectMethods() /// This may indicate a multithreading problem in the FhirPath evaluation. /// You may need to run the test in Release mode to reveal the error. /// - public static async Tasks.Task MassiveParallelSelectsShouldBeCorrect(string testName, Func> selector) + public static async Tasks.Task MassiveParallelSelectsShouldBeCorrect(string testName, Func> selector) { var actual = new ConcurrentBag<(string canonical, ValueSet resource)>(); var buffer = new BufferBlock(); @@ -126,7 +126,7 @@ private static CompiledExpression GetCompiledExpression(string expression) } - public static IEnumerable Select(this IScopedNode input, string expression, EvaluationContext ctx = null) + public static IEnumerable Select(this PocoNode input, string expression, EvaluationContext ctx = null) { var evaluator = GetCompiledExpression(expression); return evaluator(input, ctx ?? new EvaluationContext()); diff --git a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathTest.cs b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathTest.cs index 2bac05306d..a1da571c7c 100644 --- a/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathTest.cs +++ b/src/Hl7.FhirPath.R4.Tests/PocoTests/FhirPathTest.cs @@ -62,7 +62,7 @@ public void TestFhirPathTrace() Assert.AreEqual(P.DateTime.Parse("2018-05-24T14:48:00+00:00"), result.First().Value); bool traced = false; - ctx.Tracer = (string name, System.Collections.Generic.IEnumerable results) => + ctx.Tracer = (string name, System.Collections.Generic.IEnumerable results) => { System.Diagnostics.Trace.WriteLine($"{name}"); Assert.AreEqual("log", name); @@ -80,7 +80,7 @@ public void TestFhirPathTrace() Assert.IsTrue(traced); traced = false; - ctx.Tracer = (string name, System.Collections.Generic.IEnumerable results) => + ctx.Tracer = (string name, System.Collections.Generic.IEnumerable results) => { System.Diagnostics.Trace.WriteLine($"{name}"); Assert.IsTrue(name == "id" || name == "log"); diff --git a/src/Hl7.FhirPath.Tests/Functions/CollectionOperatorsTests.cs b/src/Hl7.FhirPath.Tests/Functions/CollectionOperatorsTests.cs index e7b1bd5bfd..990d995e6a 100644 --- a/src/Hl7.FhirPath.Tests/Functions/CollectionOperatorsTests.cs +++ b/src/Hl7.FhirPath.Tests/Functions/CollectionOperatorsTests.cs @@ -14,23 +14,23 @@ public class CollectionOperatorsTests [TestMethod] public void Intersect() { - var a = PocoNodeOrList.ForPrimitive("A"); - var b1 = PocoNodeOrList.ForPrimitive("B"); - var c = PocoNodeOrList.ForPrimitive("C"); - var b2 = PocoNodeOrList.ForPrimitive("B"); + var a = PocoNode.ForPrimitive("A"); + var b1 = PocoNode.ForPrimitive("B"); + var c = PocoNode.ForPrimitive("C"); + var b2 = PocoNode.ForPrimitive("B"); - var col1 = new IScopedNode[] { a, b1 }; - var col2 = new IScopedNode[] { c, b2 }; - var col3 = new IScopedNode[] { c }; + var col1 = new PocoNode[] { a, b1 }; + var col2 = new PocoNode[] { c, b2 }; + var col3 = new PocoNode[] { c }; var result = col1.Intersect(col2); Assert.IsNotNull(result); - Assert.AreEqual("B", result.First().Value); + Assert.AreEqual("B", result.First().GetValue()); result = col2.Intersect(col1); Assert.IsNotNull(result); - Assert.AreEqual("B", result.First().Value); + Assert.AreEqual("B", result.First().GetValue()); result = col1.Intersect(col3); Assert.IsNotNull(result); @@ -41,19 +41,19 @@ public void Intersect() [TestMethod] public void TestIntersect() { - IEnumerable left = PocoNodeOrList.FromList([1, 3, 3, 5, 6]); - IEnumerable right = PocoNodeOrList.FromList([3, 5, 5, 6, 8]); - PocoNodeOrList.FromList([3, 5, 6]).Should().BeEquivalentTo(left.Intersect(right).ToList()); + IEnumerable left = PocoNode.FromList([1, 3, 3, 5, 6]); + IEnumerable right = PocoNode.FromList([3, 5, 5, 6, 8]); + PocoNode.FromList([3, 5, 6]).IsEqualTo(left.Intersect(right).ToList()).Should().BeTrue(); } [TestMethod] public void TestExclude() { - IEnumerable left = - PocoNodeOrList.FromList([1, 3, 3, 5, 6]); - IEnumerable right = - PocoNodeOrList.FromList([5, 6]); - PocoNodeOrList.FromList([1, 3, 3]).Should().BeEquivalentTo(left.Exclude(right).ToList()); + IEnumerable left = + PocoNode.FromList([1, 3, 3, 5, 6]); + IEnumerable right = + PocoNode.FromList([5, 6]); + PocoNode.FromList([1, 3, 3]).IsEqualTo(left.Exclude(right).ToList()).Should().BeTrue(); } } } diff --git a/src/Hl7.FhirPath.Tests/Functions/FunctionsTests.cs b/src/Hl7.FhirPath.Tests/Functions/FunctionsTests.cs index 0b1e5ec28a..9cf024e998 100644 --- a/src/Hl7.FhirPath.Tests/Functions/FunctionsTests.cs +++ b/src/Hl7.FhirPath.Tests/Functions/FunctionsTests.cs @@ -500,7 +500,7 @@ public static IEnumerable AllFunctionTestcases() [DynamicData(nameof(AllFunctionTestcases), DynamicDataSourceType.Method)] public void AssertTestcases(string expression, bool expected, bool invalid = false) { - IScopedNode dummy = PocoNodeOrList.ForPrimitive(true); + PocoNode dummy = PocoNode.ForPrimitive(true); if (invalid) { @@ -541,7 +541,7 @@ public void SingleScalarTest() { iterations++; - return PocoNodeOrList.ForPrimitive(iterations); + return PocoNode.ForPrimitive(iterations); }); var expression = new FhirPathCompiler(symbols).Compile("once()"); diff --git a/src/Hl7.FhirPath.Tests/Functions/StringFunctionsTests.cs b/src/Hl7.FhirPath.Tests/Functions/StringFunctionsTests.cs index 3c4dbc38de..659418abd8 100644 --- a/src/Hl7.FhirPath.Tests/Functions/StringFunctionsTests.cs +++ b/src/Hl7.FhirPath.Tests/Functions/StringFunctionsTests.cs @@ -14,13 +14,13 @@ public class StringFunctionsTests [TestMethod] public void StringSplit() { - CollectionAssert.AreEqual(new[] { "A", "B", "C" }, StringOperators.FpSplit("A,B,C", ",").Select(r => r.Value.ToString()).ToArray()); + CollectionAssert.AreEqual(new[] { "A", "B", "C" }, StringOperators.FpSplit("A,B,C", ",").Select(r => r.GetValue()!.ToString()).ToArray()); // verify the empty string - CollectionAssert.AreEqual(new[] { "A", "", "C" }, StringOperators.FpSplit("A,,C", ",").Select(r => r.Value.ToString()).ToArray()); + CollectionAssert.AreEqual(new[] { "A", "", "C" }, StringOperators.FpSplit("A,,C", ",").Select(r => r.GetValue()!.ToString()).ToArray()); // Verify dups aren't removed - CollectionAssert.AreEqual(new[] { "A", "B", "C", "C" }, StringOperators.FpSplit("A,B,C,C", ",").Select(r => r.Value.ToString()).ToArray()); + CollectionAssert.AreEqual(new[] { "A", "B", "C", "C" }, StringOperators.FpSplit("A,B,C,C", ",").Select(r => r.GetValue()!.ToString()).ToArray()); // The test from the spec Assert.AreEqual(5, StringOperators.FpSplit("Peter,James,Jim,Peter,James", ",").Count()); diff --git a/src/Hl7.FhirPath.Tests/Tests/BasicFunctionTests.cs b/src/Hl7.FhirPath.Tests/Tests/BasicFunctionTests.cs index 0878580cfc..0ab8a46b03 100644 --- a/src/Hl7.FhirPath.Tests/Tests/BasicFunctionTests.cs +++ b/src/Hl7.FhirPath.Tests/Tests/BasicFunctionTests.cs @@ -10,6 +10,7 @@ //extern alias dstu2; using Hl7.Fhir.ElementModel; +using Hl7.Fhir.Model; using Hl7.FhirPath.Functions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -215,7 +216,7 @@ public void TestLogicalShortcut() [TestMethod] public void StringConcatenationAndEmpty() { - ITypedElement dummy = ElementNode.ForPrimitive(true); + PocoNode dummy = PocoNode.ForAnyPrimitive(true); Assert.AreEqual("ABCDEF", dummy.Scalar("'ABC' + '' + 'DEF'")); Assert.AreEqual("DEF", dummy.Scalar("'' + 'DEF'")); @@ -283,21 +284,21 @@ public void TestDivZero() [TestMethod] public void TestStringJoin() { - var dummy = ElementNode.CreateList("This ", "is ", "one ", "sentence", ".").ToScopedNodes(); + var dummy = PocoNode.FromList(["This ", "is ", "one ", "sentence", "."]); var result = dummy.FpJoin(string.Empty); Assert.IsNotNull(result); Assert.AreEqual("This is one sentence.", result); - dummy = ElementNode.CreateList("a", "b", "c").ToScopedNodes(); + dummy = PocoNode.FromList(["a", "b", "c"]); result = dummy.FpJoin(); Assert.IsNotNull(result); Assert.AreEqual("abc", result); - dummy = ElementNode.CreateList().ToScopedNodes(); + dummy = PocoNode.FromList([]); result = dummy.FpJoin(string.Empty); Assert.AreEqual(string.Empty, result); - dummy = ElementNode.CreateList("This", "is", "a", "separated", "sentence.").ToScopedNodes(); + dummy = PocoNode.FromList(["This", "is", "a", "separated", "sentence."]); result = dummy.FpJoin(";"); Assert.IsNotNull(result); Assert.AreEqual("This;is;a;separated;sentence.", result); @@ -307,7 +308,7 @@ public void TestStringJoin() [ExpectedException(typeof(InvalidOperationException))] public void TestStringJoinError() { - var dummy = ElementNode.CreateList("This", "is", "sentence", "with", 1, "number.").ToScopedNodes(); + var dummy = PocoNode.FromList(["This", "is", "sentence", "with", 1, "number."]); dummy.FpJoin(string.Empty); } } diff --git a/src/Hl7.FhirPath.Tests/Tests/CastTests.cs b/src/Hl7.FhirPath.Tests/Tests/CastTests.cs index 93a0c84b4c..e27c60fcbb 100644 --- a/src/Hl7.FhirPath.Tests/Tests/CastTests.cs +++ b/src/Hl7.FhirPath.Tests/Tests/CastTests.cs @@ -23,23 +23,23 @@ namespace Hl7.FhirPath.Tests [TestClass] public class CastTests { - private static readonly IScopedNode complex = new ComplexValue().ToScopedNode(); - private static readonly IEnumerable collection = ElementNode.CreateList(4, 5, complex).ToScopedNodes(); - private static readonly IEnumerable singleV = ElementNode.CreateList(4L).ToScopedNodes(); - private static readonly IEnumerable singleC = ElementNode.CreateList(complex).ToScopedNodes(); - private static readonly IEnumerable emptyColl = []; + private static readonly PocoNode complex = new DynamicDataType{DynamicTypeName = "NotAPrimitiveType"}.ToPocoNode(); + private static readonly IEnumerable collection = PocoNode.FromAnyList([4, 5, complex]); + private static readonly IEnumerable singleV = PocoNode.FromAnyList([4L]); + private static readonly IEnumerable singleC = PocoNode.FromAnyList([complex]); + private static readonly IEnumerable emptyColl = []; [TestMethod] public void TestUnbox() { Assert.IsNull(Typecasts.UnboxTo(emptyColl, typeof(string))); - collection.SequenceEqual(Typecasts.UnboxTo(collection, typeof(IEnumerable)) as IEnumerable); - Assert.AreEqual(complex, Typecasts.UnboxTo(singleC, typeof(IScopedNode))); + collection.SequenceEqual(Typecasts.UnboxTo(collection, typeof(IEnumerable)) as IEnumerable); + Assert.AreEqual(complex, Typecasts.UnboxTo(singleC, typeof(PocoNode))); Assert.AreEqual(4L, Typecasts.UnboxTo(singleV, typeof(long))); - Assert.AreEqual(4L, Typecasts.UnboxTo(PocoNodeOrList.ForPrimitive(4L), typeof(long))); + Assert.AreEqual(4L, Typecasts.UnboxTo(PocoNode.ForPrimitive(4L), typeof(long))); - Assert.AreEqual(complex, Typecasts.UnboxTo(complex, typeof(IScopedNode))); + Assert.AreEqual(complex, Typecasts.UnboxTo(complex, typeof(PocoNode))); Assert.IsNull(Typecasts.UnboxTo(null, typeof(string))); Assert.AreEqual(4L, Typecasts.UnboxTo(4L, typeof(long))); Assert.AreEqual("hi!", Typecasts.UnboxTo("hi!", typeof(string))); @@ -49,8 +49,8 @@ public void TestUnbox() public void CastFromNull() { checkCast(null, null); - checkCast>(null, []); - checkCast(null, null); + checkCast>(null, []); + checkCast(null, null); Assert.IsFalse(Typecasts.CanCastTo(null, typeof(bool))); checkCast(null, null); checkCast(null, null); @@ -60,8 +60,8 @@ public void CastFromNull() public void CastCollection() { checkCast(collection, collection); - checkCast>(collection, collection); - Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(IScopedNode))); + checkCast>(collection, collection); + Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(PocoNode))); Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(bool))); Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(bool?))); Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(string))); @@ -72,10 +72,10 @@ public void CastComplex() { checkCast(complex, complex); - Assert.IsTrue(Typecasts.CanCastTo(complex, typeof(IEnumerable))); - var result = (IEnumerable)Typecasts.CastTo(complex, typeof(IEnumerable)); + Assert.IsTrue(Typecasts.CanCastTo(complex, typeof(IEnumerable))); + var result = (IEnumerable)Typecasts.CastTo(complex, typeof(IEnumerable)); Assert.AreEqual(complex, result.Single()); - checkCast(complex, complex); + checkCast(complex, complex); Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(bool))); Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(bool?))); Assert.IsFalse(Typecasts.CanCastTo(collection, typeof(string))); @@ -86,13 +86,13 @@ public void CastValue() { checkCast(4L, 4L); - Assert.IsTrue(Typecasts.CanCastTo(4, typeof(IEnumerable))); - var result = (IEnumerable)Typecasts.CastTo(4L, typeof(IEnumerable)); - Assert.AreEqual(4L, result.Single().Value); + Assert.IsTrue(Typecasts.CanCastTo(4, typeof(IEnumerable))); + var result = (IEnumerable)Typecasts.CastTo(4L, typeof(IEnumerable)); + Assert.AreEqual(4L, result.Single().GetValue()); - Assert.IsTrue(Typecasts.CanCastTo(4L, typeof(IScopedNode))); - var result2 = (IScopedNode)Typecasts.CastTo(4L, typeof(IScopedNode)); - Assert.AreEqual(4L, result2.Value); + Assert.IsTrue(Typecasts.CanCastTo(4L, typeof(PocoNode))); + var result2 = (PocoNode)Typecasts.CastTo(4L, typeof(PocoNode)); + Assert.AreEqual(4L, result2.GetValue()); checkCast(true, true); checkCast(4L, 4m); @@ -111,13 +111,13 @@ public void CastNullable() { checkCast("hi", "hi"); - Assert.IsTrue(Typecasts.CanCastTo("hi", typeof(IEnumerable))); - var result = (IEnumerable)Typecasts.CastTo("hi", typeof(IEnumerable)); - Assert.AreEqual("hi", result.Single().Value); + Assert.IsTrue(Typecasts.CanCastTo("hi", typeof(IEnumerable))); + var result = (IEnumerable)Typecasts.CastTo("hi", typeof(IEnumerable)); + Assert.AreEqual("hi", result.Single().GetValue()); - Assert.IsTrue(Typecasts.CanCastTo("hi", typeof(IScopedNode))); - var result2 = (IScopedNode)Typecasts.CastTo("hi", typeof(IScopedNode)); - Assert.AreEqual("hi", result2.Value); + Assert.IsTrue(Typecasts.CanCastTo("hi", typeof(PocoNode))); + var result2 = (PocoNode)Typecasts.CastTo("hi", typeof(PocoNode)); + Assert.AreEqual("hi", result2.GetValue()); checkCast(true, true); checkCast(4L, 4m); @@ -137,43 +137,4 @@ private void checkCast(object source, T value) } } - - internal class ComplexValue : ITypedElement - { - public string Name - { - get - { - return null; - } - } - - public string Location - { - get - { - return null; - } - } - - public string InstanceType - { - get - { - return "NotAPrimitiveType"; - } - } - - public object Value - { - get - { - return null; - } - } - - public IElementDefinitionSummary Definition => null; - - public IEnumerable Children(string name = null) => []; - } } \ No newline at end of file diff --git a/src/Hl7.FhirPath.Tests/Tests/ConversionsTests.cs b/src/Hl7.FhirPath.Tests/Tests/ConversionsTests.cs index e76b05c5bf..d8b01ecb06 100644 --- a/src/Hl7.FhirPath.Tests/Tests/ConversionsTests.cs +++ b/src/Hl7.FhirPath.Tests/Tests/ConversionsTests.cs @@ -112,12 +112,12 @@ public void ConvertToQuantity() var vals = new[] {new P.Quantity(5m), new P.Quantity(75m), new P.Quantity(75.6m, P.Quantity.UCUM_UNIT), new P.Quantity(30m,"wk"), new P.Quantity(0.0m), new P.Quantity(1.0m), new P.Quantity(80m, "kg") }; - + inputs.Zip(vals, (i, v) => (i, v)) .ToList() .ForEach(c => Assert.AreEqual(c.v, c.i.ToQuantity())); inputs.ToList().ForEach(c => Assert.IsTrue(c.ConvertsToQuantity())); - + var wrong = create("hi", "++6", "2,6", "no", "false",DateTimeOffset.Now); wrong.ForEach(c => Assert.IsNull(c.ToQuantity())); wrong.ForEach(c => Assert.IsFalse(c.ConvertsToQuantity())); @@ -215,27 +215,27 @@ public void ConvertToTime() [TestMethod] public void CheckTypeDetermination() { - var values = ElementNode.CreateList(1, 1L, true, "hi", 4.0m, 4.0f, P.DateTime.Now()).ToScopedNodes(); - - Test.IsInstanceOfType(values.Item(0).Single().Value, typeof(int)); - Test.IsInstanceOfType(values.Item(0).Single().Value, typeof(long)); - Test.IsInstanceOfType(values.Item(1).Single().Value, typeof(bool)); - Test.IsInstanceOfType(values.Item(2).Single().Value, typeof(string)); - Test.IsInstanceOfType(values.Item(3).Single().Value, typeof(decimal)); - Test.IsInstanceOfType(values.Item(4).Single().Value, typeof(decimal)); - Test.IsInstanceOfType(values.Item(5).Single().Value, typeof(P.DateTime)); + var values = PocoNode.FromAnyList([1, 1L, true, "hi", 4.0m, 4.0f, P.DateTime.Now()]); + + Test.IsInstanceOfType(values.Item(0).Single().GetValue(), typeof(int)); + Test.IsInstanceOfType(values.Item(0).Single().GetValue(), typeof(long)); + Test.IsInstanceOfType(values.Item(1).Single().GetValue(), typeof(bool)); + Test.IsInstanceOfType(values.Item(2).Single().GetValue(), typeof(string)); + Test.IsInstanceOfType(values.Item(3).Single().GetValue(), typeof(decimal)); + Test.IsInstanceOfType(values.Item(4).Single().GetValue(), typeof(decimal)); + Test.IsInstanceOfType(values.Item(5).Single().GetValue(), typeof(P.DateTime)); } [TestMethod] public void TestItemSelection() { - var values = ElementNode.CreateList(1L, 2, 3L, 4, 5, 6, 7).ToScopedNodes(); + var values = PocoNode.FromAnyList([1L, 2, 3L, 4, 5, 6, 7]); - Assert.AreEqual(1L, values.Item(0).Single().Value); - Assert.AreEqual(2, values.Item(1).Single().Value); - Assert.AreEqual(3L, values.Item(2).Single().Value); - Assert.AreEqual(1L, values.First().Value); + Assert.AreEqual(1L, values.Item(0).Single().GetValue()); + Assert.AreEqual(2, values.Item(1).Single().GetValue()); + Assert.AreEqual(3L, values.Item(2).Single().GetValue()); + Assert.AreEqual(1L, values.First().GetValue()); Assert.IsFalse(values.Item(100).Any()); } diff --git a/src/Hl7.FhirPath.Tests/Tests/EnviromentTests.cs b/src/Hl7.FhirPath.Tests/Tests/EnviromentTests.cs index c9c88195be..ece67012ea 100644 --- a/src/Hl7.FhirPath.Tests/Tests/EnviromentTests.cs +++ b/src/Hl7.FhirPath.Tests/Tests/EnviromentTests.cs @@ -17,7 +17,7 @@ public void TestEnvironment() var compiler = new FhirPathCompiler(); var expr = compiler.Compile("%var = 1"); - expr.IsTrue(null!, new EvaluationContext { Environment = new Dictionary> {{ "var", PocoNodeOrList.ForPrimitive(1) }}} ).Should().BeTrue(); - expr.IsTrue(null!, new EvaluationContext { Environment = new Dictionary> {{ "var", PocoNodeOrList.ForPrimitive(2)}}} ).Should().BeFalse(); + expr.IsTrue(PocoNode.ForAnyPrimitive(true), new EvaluationContext { Environment = new Dictionary> {{ "var", PocoNode.ForPrimitive(1) }}} ).Should().BeTrue(); + expr.IsTrue(PocoNode.ForAnyPrimitive(true), new EvaluationContext { Environment = new Dictionary> {{ "var", PocoNode.ForPrimitive(2)}}} ).Should().BeFalse(); } } \ No newline at end of file