private static void TypedElementPerformance(ITypedElement nav) { // run extraction once to allow for caching extract(); //System.Threading.Thread.Sleep(20000); var sw = new Stopwatch(); sw.Start(); for (var i = 0; i < 5_000; i++) { extract(); } sw.Stop(); Debug.WriteLine($"Navigating took {sw.ElapsedMilliseconds / 5 } micros"); void extract() { var usual = nav.Children("identifier").First().Children("use").First().Value; var phone = nav.Children("telecom").First().Children("system").First().Value; var prefs = nav.Children("communication").Where(c => c.Children("preferred").Any(pr => pr.Value is string s && s == "true")).Count(); var link = nav.Children("link").Children("other").Children("reference"); } }
public static ResourceReference ParseResourceReference(this ITypedElement instance) { return(new ResourceReference() { Reference = instance.Children("reference").GetString(), Display = instance.Children("display").GetString() }); }
public static bool IsEmptyElement(ITypedElement element) { if (element is EmptyElement) { return(true); } return(element.Children().Count() == 1 && element.Children("meta").Count() == 1); }
/// <summary> /// Returns the Id of the resource, if the current node is a resource /// </summary> /// <returns></returns> public string Id() { if (_cache.Id == null) { _cache.Id = AtResource ? "#" + Current.Children("id").FirstOrDefault()?.Value as string : null; } return(_cache.Id); }
public static CodeableConcept ParseCodeableConcept(this ITypedElement instance) { return(new CodeableConcept() { Coding = instance.Children("coding").Select(codingNav => codingNav.ParseCoding()).ToList(), Text = instance.Children("text").GetString() }); }
public static Coding ParseCoding(this ITypedElement instance) { return(new Coding() { System = instance.Children("system").GetString(), Version = instance.Children("version").GetString(), Code = instance.Children("code").GetString(), Display = instance.Children("display").GetString(), UserSelected = instance.Children("userSelected").SingleOrDefault()?.Value as bool? }); }
internal static Fhir.Model.Primitives.Quantity?ParseQuantity(ITypedElement 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 Fhir.Model.Primitives.Quantity(value.Value, unit)); }
public static bool IsExactlyEqualTo(this ITypedElement left, ITypedElement right) { 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); } return(childrenL.Zip(childrenR, (childL, childR) => childL.Name == childR.Name && childL.IsExactlyEqualTo(childR)).All(t => t)); }
private void InitializeNodeCacheRecursive(ITypedElement node) { foreach (var child in node.Children()) { // Cache instance type if (_typeToNodeLookUp.ContainsKey(child.InstanceType)) { _typeToNodeLookUp[child.InstanceType].Add(child); } else { _typeToNodeLookUp[child.InstanceType] = new List <ITypedElement> { child }; } // Cache name if (_nameToNodeLookUp.ContainsKey(child.Name)) { _nameToNodeLookUp[child.Name].Add(child); } else { _nameToNodeLookUp[child.Name] = new List <ITypedElement> { child }; } // Recursively process node's children except resource children, which will be processed independently as a resource if (!child.IsFhirResource()) { InitializeNodeCacheRecursive(child); } } }
public static ExpandoObject ToExpando(this ITypedElement element) { var result = new ExpandoObject(); if (element.Value is not null) { result.TryAdd("value", element.Value); } var children = element.Children().ToArray(); for (int ix = 0; ix < children.Length; ix++) { if (children[ix].Definition.IsCollection) { var childlist = new List <ExpandoObject>(); var collectionName = children[ix].Name; do { childlist.Add(children[ix].ToExpando()); ix++; }while (ix < children.Length && children[ix].Name == collectionName); result.TryAdd(collectionName, childlist); } else { result.TryAdd(children[ix].Name, children[ix].ToExpando()); } } return(result); }
public static IEnumerable <ITypedElement> Navigate(this ITypedElement 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") // Also we do some poor polymorphism here: Resource.meta.lastUpdated is also allowed. var baseClasses = new[] { "Resource", "DomainResource" }; if (element.InstanceType == name || baseClasses.Contains(name)) { return(new List <ITypedElement>() { element }); } else { return(Enumerable.Empty <ITypedElement>()); } } else { return(element.Children(name)); } }
private static bool IsEmptyNode(ITypedElement node) { EnsureArg.IsNotNull(node); // A node is considered empty when it has no children and its value is null return(!node.Children().Any() && node.Value == null && !node.IsFhirResource()); }
public static bool IsEquivalentTo(this ITypedElement left, ITypedElement right, bool compareNames = false) { if (compareNames && !namesAreEquivalent(left, right)) { return(false); } var l = left.Value; var r = right.Value; // TODO: this is actually a cast with knowledge of FHIR->System mappings, we don't want that here anymore // Convert quantities if (left.InstanceType == "Quantity" && l == null) { l = Typecasts.ParseQuantity(left); } if (right.InstanceType == "Quantity" && r == null) { r = Typecasts.ParseQuantity(right); } // Compare primitives (or extended primitives) // TODO: Define IsEquivalentTo for ALL datatypes in ITypedElement.value and move to Support assembly + test // TODO: Define on object, so this switch can be removed here // TODO: Move this IsEquivalentTo to the ElementModel assembly // Maybe create an interface? if (l != null && r != null) { return(Any.IsEquivalentTo(l, r)); } else if (l == null && r == null) { // Compare complex types (extensions on primitives are not compared, but handled (=ignored) above var childrenL = left.Children(); var childrenR = right.Children(); return(childrenL.IsEquivalentTo(childrenR, compareNames: true)); // NOTE: Assumes null will never be returned when any() children exist } else { // Else, we're comparing a complex (without a value) to a primitive which (probably) should return false return(false); } bool namesAreEquivalent(ITypedElement le, ITypedElement ri) { if (le.Name == "id" && ri.Name == "id") { return(true); // don't compare 'id' elements for equivalence } if (le.Name != ri.Name) { return(false); } return(true); } }
private void addChildren(ITypedElement node, JObject parent) { var resourceTypeIndicator = node.Annotation <IResourceTypeSupplier>()?.ResourceType; var isResource = node.Definition?.IsResource ?? resourceTypeIndicator != null; var containedResourceType = isResource ? (node.InstanceType ?? resourceTypeIndicator) : null; if (containedResourceType != null) { parent.AddFirst(new JProperty(JsonSerializationDetails.RESOURCETYPE_MEMBER_NAME, containedResourceType)); } foreach (var nameGroup in node.Children().GroupBy(n => n.Name)) { var members = nameGroup.ToList(); // serialization info should be the same for each element in an // array - but do not explicitly check that if (!MustSerializeMember(members[0], out var generalInfo)) { break; } bool hasTypeInfo = generalInfo != null; // If we have type information, we know whather we need an array. // failing that, check whether this is a roundtrip and we have the information // about arrays in the serialization deails. Failing that, assume the default: // for unknown properties is to use an array - safest bet. var generalJsonDetails = members[0].GetJsonSerializationDetails(); var hasIndex = generalJsonDetails?.ArrayIndex != null; var needsArray = generalInfo?.IsCollection ?? hasIndex; var children = members.Select(m => buildNode(m)) .Where(c => !(c.first == null && c.second == null)).ToList(); // Don't add empty nodes to the parent if (!children.Any()) { continue; } var needsMainProperty = children.Any(c => c.first != null); var needsShadowProperty = children.Any(c => c.second != null); var propertyName = generalInfo?.IsChoiceElement == true ? members[0].Name + members[0].InstanceType.Capitalize() : members[0].Name; if (needsMainProperty) { parent.Add(new JProperty(propertyName, needsArray ? new JArray(children.Select(c => c.first ?? JValue.CreateNull())) : children[0].first)); } if (needsShadowProperty) { parent.Add(new JProperty("_" + propertyName, needsArray ? new JArray(children.Select(c => (JToken)c.second ?? JValue.CreateNull())) : (JToken)children[0].second)); } } }
private static void visit(this ITypedElement root, Action <int, ITypedElement> visitor, int depth = 0) { visitor(depth, root); foreach (var child in root.Children()) { visit(child, visitor, depth + 1); } }
public ProfilePreprocessor(Func <string, StructureDefinition> profileResolver, Func <StructureDefinition, OperationOutcome> snapshotGenerator, ITypedElement instance, string declaredTypeProfile, IEnumerable <StructureDefinition> additionalProfiles, IEnumerable <string> additionalCanonicals) { _profileResolver = profileResolver; _snapshotGenerator = snapshotGenerator; _path = instance.Location; _profiles = new ProfileAssertion(_path, _profileResolver); if (instance.InstanceType != null) { _profiles.SetInstanceType(ModelInfo.CanonicalUriForFhirCoreType(instance.InstanceType)); } if (declaredTypeProfile != null) { _profiles.SetDeclaredType(declaredTypeProfile); } // This is only for resources, but I don't bother checking, since this will return empty anyway _profiles.AddStatedProfile(instance.Children("meta").Children("profile").Select(p => p.Value).Cast <string>()); //Almost identically, extensions can declare adherance to a profile using the 'url' attribute if (declaredTypeProfile == ModelInfo.CanonicalUriForFhirCoreType(FHIRDefinedType.Extension)) { var urlDeclaration = instance.Children("url").FirstOrDefault()?.Value as string; if (urlDeclaration != null && urlDeclaration.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) { _profiles.AddStatedProfile(urlDeclaration); } } if (additionalProfiles != null) { _profiles.AddStatedProfile(additionalProfiles); } if (additionalCanonicals != null) { _profiles.AddStatedProfile(additionalCanonicals); } }
public static void ProducedCorrectTypedLocations(ITypedElement patient) { string getPretty(ITypedElement n) => n.Annotation <IShortPathGenerator>().ShortPath; Assert.AreEqual("Patient", getPretty(patient)); var patientC = patient.Children().ToList(); Assert.AreEqual("Patient.id", getPretty(patient.Children("id").First())); var ids = patient.Children("identifier").ToList(); Assert.AreEqual("Patient.identifier[0]", getPretty(ids[0])); Assert.AreEqual("Patient.identifier[0].use", getPretty(ids[0].Children("use").Single())); Assert.AreEqual("Patient.identifier[1]", getPretty(ids[1])); Assert.AreEqual("Patient.identifier[1].use", getPretty(ids[1].Children("use").Single())); Assert.AreEqual("Patient.deceased", getPretty(patient.Children("deceased").Single())); Assert.AreEqual("Patient.contained[0].name[1].use", getPretty(patient.Children("contained").First().Children("name").Skip(1) .First().Children("use").Single())); }
/// <summary> /// Parses a bindeable type (code, Coding, CodeableConcept, Quantity, string, uri) into a FHIR coded datatype. /// Extensions will be parsed from the 'value' of the (simple) extension. /// </summary> /// <param name="instance"></param> /// <returns>An Element of a coded type (code, Coding or CodeableConcept) dependin on the instance type, /// or null if no bindable instance data was found</returns> /// <remarks>The instance type is mapped to a codable type as follows: /// 'code' => code /// 'Coding' => Coding /// 'CodeableConcept' => CodeableConcept /// 'Quantity' => Coding /// 'Extension' => depends on value[x] /// 'string' => code /// 'uri' => code /// </remarks> public static Element ParseBindable(this ITypedElement instance) { var instanceType = ModelInfo.FhirTypeNameToFhirType(instance.InstanceType); switch (instanceType) { case FHIRDefinedType.Code: return(instance.ParsePrimitive <Code>()); case FHIRDefinedType.Coding: return(instance.ParseCoding()); case FHIRDefinedType.CodeableConcept: return(instance.ParseCodeableConcept()); case FHIRDefinedType.Quantity: return(parseQuantity(instance)); case FHIRDefinedType.String: return(new Code(instance.ParsePrimitive <FhirString>()?.Value)); case FHIRDefinedType.Uri: return(new Code(instance.ParsePrimitive <FhirUri>()?.Value)); case FHIRDefinedType.Extension: return(parseExtension(instance)); case null: //HACK: fall through - IElementNav did not provide a type //should not happen, and I have no intention to handle it. default: // Not bindable return(null); } Coding parseQuantity(ITypedElement nav) { var newCoding = new Coding(); var q = instance.ParseQuantity(); newCoding.Code = q.Unit; newCoding.System = q.System ?? "http://unitsofmeasure.org"; return(newCoding); } Element parseExtension(ITypedElement nav) { // HACK: For now, assume this is a typed navigator, so we have "value", // not the unparsed "valueCode" etc AND we have Type (in ParseBindable()) var valueChild = instance.Children("value").FirstOrDefault(); return(valueChild?.ParseBindable()); } }
/// <summary> /// Get property value from ITypedElement. /// </summary> /// <param name="element">self element.</param> /// <param name="propertyName">property name.</param> /// <returns>property value.</returns> public static object GetPropertyValue( this ITypedElement element, string propertyName) { if (element == null || string.IsNullOrEmpty(propertyName)) { return(null); } return(element.Children(propertyName).FirstOrDefault()?.Value); }
public static IEnumerable <ITypedElement> Descendants(this ITypedElement element) { foreach (var child in element.Children()) { yield return(child); foreach (var grandchild in child.Descendants()) { yield return(grandchild); } } }
public static DateTime?GetLastUpdatedDay(this ITypedElement element) { var result = element?.Children(FhirBundleConstants.MetaKey).FirstOrDefault()?.GetPropertyValue(FhirBundleConstants.LastUpdatedKey); if (result == null) { return(null); } var lastUpdateDatetime = DateTimeOffset.Parse(result.ToString()); return(new DateTime(lastUpdateDatetime.Year, lastUpdateDatetime.Month, lastUpdateDatetime.Day)); }
public static Model.Quantity ParseQuantity(this ITypedElement instance) { var newQuantity = new Quantity { Value = instance.Children("value").SingleOrDefault()?.Value as decimal?, Unit = instance.Children("unit").GetString(), System = instance.Children("system").GetString(), Code = instance.Children("code").GetString() }; var comp = instance.Children("comparator").GetString(); if (comp != null) { newQuantity.ComparatorElement = new Code <Quantity.QuantityComparator> { ObjectValue = comp } } ; return(newQuantity); }
public static bool IsEqualTo(this ITypedElement left, ITypedElement right, bool compareNames = false) { // TODO: Merge with ElementNodeComparator.IsEqualTo if (compareNames && (left.Name != right.Name)) { return(false); } var l = CastIntToLong(left.Value); var r = CastIntToLong(right.Value); // TODO: this is actually a cast with knowledge of FHIR->System mappings, we don't want that here anymore // Convert quantities if (left.InstanceType == "Quantity" && l == null) { l = Typecasts.ParseQuantity(left); } if (right.InstanceType == "Quantity" && r == null) { r = Typecasts.ParseQuantity(right); } // Compare primitives (or extended primitives) if (l != null && r != null) { return(Any.IsEqualTo(l, r)); } else if (l == null && r == null) { // Compare complex types (extensions on primitives are not compared, but handled (=ignored) above var childrenL = left.Children(); var childrenR = right.Children(); return(childrenL.IsEqualTo(childrenR, compareNames: true)); // NOTE: Assumes null will never be returned when any() children exist } else { // Else, we're comparing a complex (without a value) to a primitive which (probably) should return false return(false); } object CastIntToLong(object val) { // [MV 20200128] Because FhirPath works with longs, integers will be cast to longs return(val?.GetType() == typeof(int) ? Convert.ToInt64(val) : val); } }
/// <summary> /// Compares two <see cref="ITypedElement"/> trees. /// </summary> /// <param name="expected">The tree that contains the expected, "correct" data.</param> /// <param name="actual">The tree to compare against the <paramref name="expected"/> tree.</param> /// <returns>A <see cref="TreeComparisonResult"/> that summarizes the differences between the trees.</returns> public static TreeComparisonResult IsEqualTo(this ITypedElement expected, ITypedElement actual) { if (expected.Name != actual.Name) { return(TreeComparisonResult.Fail(actual.Location, $"name: was '{actual.Name}', expected '{expected.Name}'")); } if (!Object.Equals(expected.Value, actual.Value)) { return(TreeComparisonResult.Fail(actual.Location, $"value: was '{actual.Value}', expected '{expected.Value}'")); } if (expected.InstanceType != actual.InstanceType && actual.InstanceType != null) { return(TreeComparisonResult.Fail(actual.Location, $"type: was '{actual.InstanceType}', expected '{expected.InstanceType}'")); } if (expected.Location != actual.Location) { TreeComparisonResult.Fail(actual.Location, $"Path: was '{actual.Location}', expected '{expected.Location}'"); } // Ignore ordering (only relevant to xml) var childrenExp = expected.Children().OrderBy(e => e.Name); var childrenActual = actual.Children().OrderBy(e => e.Name).GetEnumerator(); // Don't compare lengths, as this would require complete enumeration of both collections // just enumerate through the lists, comparing each item as they go. // first fail (or list end) will drop out. foreach (var exp in childrenExp) { if (!childrenActual.MoveNext()) { TreeComparisonResult.Fail(actual.Location, $"number of children was different"); } var result = exp.IsEqualTo(childrenActual.Current); if (!result.Success) { return(result); } } if (childrenActual.MoveNext()) { TreeComparisonResult.Fail(actual.Location, $"number of children was different"); } return(TreeComparisonResult.OK); }
public static IEnumerable <ITypedElement> ResourceDescendantsWithoutSubResource(this ITypedElement node) { foreach (var child in node.Children()) { // Skip sub resources in bundle entry and contained list if (child.IsFhirResource()) { continue; } yield return(child); foreach (var n in child.ResourceDescendantsWithoutSubResource()) { yield return(n); } } }
public static MatchResult Match(ElementDefinitionNavigator definitionParent, ITypedElement instanceParent) { var definitionElements = harvestDefinitionNames(definitionParent); var elementsToMatch = instanceParent.Children().Cast <ScopedNode>().ToList(); List <Match> matches = new List <Match>(); foreach (var definitionElement in definitionElements) { var match = new Match() { Definition = definitionElement, InstanceElements = new List <ITypedElement>() }; // Special case is the .value of a primitive fhir type, this is represented // as the "Value" of the IValueProvider interface, not as a real child if (definitionElement.Current.IsPrimitiveValueConstraint()) { if (instanceParent.Value != null) { match.InstanceElements.Add(instanceParent); } } else { var definitionPath = ProfileNavigationExtensions.GetNameFromPath(definitionElement.Current?.Base?.Path ?? definitionElement.Path); var found = elementsToMatch.Where(ie => NameMatches(definitionPath, ie)).ToList(); match.InstanceElements.AddRange(found); elementsToMatch.RemoveAll(e => found.Contains(e)); } matches.Add(match); } MatchResult result = new MatchResult(); result.Matches = matches; result.UnmatchedInstanceElements = elementsToMatch; return(result); }
internal Base Deserialize(ClassMapping mapping, Base existing = null) { var mappingToUse = mapping; if (mappingToUse == null) { if (_current.InstanceType is null) { throw Error.Format("Underlying data source was not able to provide the actual instance type of the resource."); } mappingToUse = (ClassMapping)_inspector.Provide(_current.InstanceType); if (mappingToUse == null) { RaiseFormatError($"Asked to deserialize unknown type '{_current.InstanceType}'", _current.Location); } } if (existing == null) { var fac = new DefaultModelFactory(); existing = (Base)fac.Create(mappingToUse.NativeType); } else { if (mappingToUse.NativeType != existing.GetType()) { throw Error.Argument(nameof(existing), "Existing instance is of type {0}, but data indicates resource is a {1}".FormatWith(existing.GetType().Name, mappingToUse.NativeType.Name)); } } // The older code for read() assumes the primitive value member is represented as a separate child element named "value", // while the newer ITypedElement represents this as a special Value property. We simulate the old behaviour here, by // explicitly adding the value property as a child and making it act like a typed node. var members = _current.Value != null ? new[] { new ValuePropertyTypedElement(_current) }.Union(_current.Children()) : _current.Children(); read(mappingToUse, members, existing); return(existing); }
private static ElementNode buildNode(ITypedElement node, bool recursive, IEnumerable <Type> annotationsToCopy, ElementNode parent) { var me = new ElementNode(node.Name, node.Value, node.InstanceType, node.Definition) { Parent = parent }; foreach (var t in annotationsToCopy ?? Enumerable.Empty <Type>()) { foreach (var ann in node.Annotations(t)) { me.AddAnnotation(ann); } } if (recursive) { me.ChildList.AddRange(node.Children().Select(c => buildNode(c, recursive: true, annotationsToCopy: annotationsToCopy, me))); } return(me); }
public static bool Matches(this ITypedElement value, ITypedElement pattern) { if (value == null && pattern == null) { return(true); } if (value == null || pattern == null) { return(false); } if (!ValueEquality(value.Value, pattern.Value)) { return(false); } // 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)))); }
internal Base Deserialize(ClassMapping mapping, Base existing = null) { if (mapping == null) { throw Error.ArgumentNull(nameof(mapping)); } if (existing == null) { var fac = new DefaultModelFactory(); existing = (Base)fac.Create(mapping.NativeType); } else { if (mapping.NativeType != existing.GetType()) { throw Error.Argument(nameof(existing), "Existing instance is of type {0}, but data indicates resource is a {1}".FormatWith(existing.GetType().Name, mapping.NativeType.Name)); } } // The older code for read() assumes the primitive value member is represented as a separate child element named "value", // while the newer ITypedElement represents this as a special Value property. We simulate the old behaviour here, by // explicitly adding the value property as a child and making it act like a typed node. var members = _current.Value != null ? new[] { new ValuePropertyTypedElement(_current) }.Union(_current.Children()) : _current.Children(); try { read(mapping, members, existing); } catch (StructuralTypeException ste) { throw Error.Format(ste.Message); } return(existing); }