Beispiel #1
0
        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");
            }
        }
Beispiel #2
0
 public static ResourceReference ParseResourceReference(this ITypedElement instance)
 {
     return(new ResourceReference()
     {
         Reference = instance.Children("reference").GetString(),
         Display = instance.Children("display").GetString()
     });
 }
Beispiel #3
0
        public static bool IsEmptyElement(ITypedElement element)
        {
            if (element is EmptyElement)
            {
                return(true);
            }

            return(element.Children().Count() == 1 && element.Children("meta").Count() == 1);
        }
Beispiel #4
0
        /// <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);
        }
Beispiel #5
0
 public static CodeableConcept ParseCodeableConcept(this ITypedElement instance)
 {
     return(new CodeableConcept()
     {
         Coding =
             instance.Children("coding").Select(codingNav => codingNav.ParseCoding()).ToList(),
         Text = instance.Children("text").GetString()
     });
 }
Beispiel #6
0
 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);
                }
            }
        }
Beispiel #10
0
        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));
     }
 }
Beispiel #12
0
        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());
        }
Beispiel #13
0
        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);
            }
        }
Beispiel #14
0
        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));
                }
            }
        }
Beispiel #15
0
        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);
            }
        }
Beispiel #16
0
        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);
            }
        }
Beispiel #17
0
        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()));
        }
Beispiel #18
0
        /// <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);
        }
Beispiel #20
0
        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));
        }
Beispiel #22
0
        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);
            }
        }
Beispiel #24
0
        /// <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);
        }
Beispiel #25
0
        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);
        }
Beispiel #27
0
        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);
        }
Beispiel #28
0
        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);
        }