Example #1
0
        /// <summary>
        /// Tries to deserialize an object from an XElement.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="t"></param>
        /// <param name="setter"></param>
        /// <param name="newobj"></param>
        /// <param name="logWarnings">Whether warning messages should be emitted in case of missing properties.</param>
        /// <returns>True on success.</returns>
        public virtual bool TryDeserializeObject(XElement element, ITypeData t, Action <object> setter, object newobj = null, bool logWarnings = true)
        {
            if (element.IsEmpty && !element.HasAttributes)
            {
                setter(null);
                return(true);
            }
            if (newobj == null)
            {
                try
                {
                    newobj = t.CreateInstance(Array.Empty <object>());
                    t      = TypeData.GetTypeData(newobj);
                }
                catch (TargetInvocationException ex)
                {
                    if (ex.InnerException is System.ComponentModel.LicenseException)
                    {
                        throw new Exception(string.Format("Could not create an instance of '{0}': {1}", t.GetAttribute <DisplayAttribute>().Name, ex.InnerException.Message));
                    }
                    else
                    {
                        ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                    }
                }
            }

            var prevobj = Object;

            Object = newobj;
            var t2 = t;

            if (newobj == null)
            {
                throw new ArgumentNullException(nameof(newobj));
            }
            var properties = t2.GetMembers()
                             .Where(x => x.HasAttribute <XmlIgnoreAttribute>() == false)
                             .ToArray();

            try
            {
                foreach (var prop in properties)
                {
                    var attr = prop.GetAttribute <XmlAttributeAttribute>();
                    if (attr == null)
                    {
                        continue;
                    }
                    var name       = string.IsNullOrWhiteSpace(attr.AttributeName) ? prop.Name : attr.AttributeName;
                    var attr_value = element.Attribute(XmlConvert.EncodeLocalName(name));
                    var p          = prop as MemberData;

                    if (p != null && attr_value != null && p.Member is PropertyInfo csprop)
                    {
                        try
                        {
                            readContentInternal(csprop.PropertyType, false, () => attr_value.Value, element, out object value);

                            p.SetValue(newobj, value);
                        }
                        catch (Exception e)
                        {
                            if (logWarnings)
                            {
                                Log.Warning(element, "Attribute value '{0}' was not read correctly as a {1}", attr_value.Value, p);
                                Log.Debug(e);
                            }
                        }
                    }
                }

                if (properties.FirstOrDefault(x => x.HasAttribute <XmlTextAttribute>()) is IMemberData mem2)
                {
                    object value;
                    if (mem2.TypeDescriptor is TypeData td &&
                        readContentInternal(td.Load(), false, () => element.Value, element, out object _value))
                    {
                        value = _value;
                    }
                    else
                    {
                        value = StringConvertProvider.FromString(element.Value, mem2.TypeDescriptor, null);
                    }
                    mem2.SetValue(newobj, value);
                }
                else
                {
                    var    props    = properties.ToLookup(x => x.GetAttributes <XmlElementAttribute>().FirstOrDefault()?.ElementName ?? x.Name);
                    var    elements = element.Elements().ToArray();
                    bool[] visited  = new bool[elements.Length];

                    double order = 0;
                    int    foundWithCurrentType = 0;
                    while (true)
                    {
                        double nextOrder = 1000;
                        // since the object might be dynamically adding properties as other props are added.
                        // we need to iterate a bit. Example: Test Plan Reference.

                        int found = visited.Count(x => x);
                        for (int i = 0; i < elements.Length; i++)
                        {
                            var element2 = elements[i];
                            if (visited[i])
                            {
                                continue;
                            }
                            if (element2.Attribute(IgnoreMemberXName) is XAttribute attr && attr.Value == "true")
                            {
                                visited[i] = true;
                                continue;
                            }
                            IMemberData property        = null;
                            var         name            = XmlConvert.DecodeName(element2.Name.LocalName);
                            var         propertyMatches = props[name];

                            int hits = 0;
                            foreach (var p in propertyMatches)
                            {
                                if (p.Writable || p.HasAttribute <XmlIgnoreAttribute>())
                                {
                                    property = p;
                                    hits++;
                                }
                            }

                            if (0 == hits)
                            {
                                try
                                {
                                    if (property == null)
                                    {
                                        property = t2.GetMember(name);
                                    }
                                    if (property == null)
                                    {
                                        property = t2.GetMembers().FirstOrDefault(x => x.Name == name);
                                    }
                                }
                                catch { }
                                if (property == null || property.Writable == false)
                                {
                                    continue;
                                }
                                hits = 1;
                            }
                            if (hits > 1)
                            {
                                Log.Warning(element2, "Multiple properties named '{0}' are available to the serializer in '{1}' this might give issues in serialization.", element2.Name.LocalName, t.GetAttribute <DisplayAttribute>().Name);
                            }

                            if (property.GetAttribute <DeserializeOrderAttribute>() is DeserializeOrderAttribute orderAttr)
                            {
                                if (order < orderAttr.Order)
                                {
                                    if (orderAttr.Order < nextOrder)
                                    {
                                        nextOrder = orderAttr.Order;
                                    }
                                    continue;
                                }
                            }
                            else
                            {
                                nextOrder = order;
                            }

                            visited[i] = true;
                            var prev = CurrentMember;
                            CurrentMember = property;
                            try
                            {
                                if (CurrentMember.HasAttribute <XmlIgnoreAttribute>())      // This property shouldn't have been in the file in the first place, but in case it is (because of a change or a bug), we shouldn't try to set it. (E.g. SweepLoopRange.SweepStep)
                                {
                                    if (!CurrentMember.HasAttribute <BrowsableAttribute>()) // In the special case we assume that this is some compatibility property that still needs to be set if present in the XML. (E.g. TestPlanReference.DynamicDataContents)
                                    {
                                        continue;
                                    }
                                }

                                if (property is MemberData mem && mem.Member is PropertyInfo Property && Property.PropertyType.HasInterface <IList>() && Property.PropertyType.IsGenericType && Property.HasAttribute <XmlElementAttribute>())
                                {
                                    // Special case to mimic old .NET XmlSerializer behavior
                                    var             list     = (IList)Property.GetValue(newobj);
                                    Action <object> setValue = x => list.Add(x);
                                    Serializer.Deserialize(element2, setValue, Property.PropertyType.GetGenericArguments().First());
                                }
                                else
                                {
                                    Action <object> setValue = x =>
                                    {
                                        var current = property.GetValue(newobj);
                                        property.SetValue(newobj, x);
                                        if (false == Equals(current, x))
                                        { // for some value-like type, it may be needed
                                            // to set the parent object when a property is changed
                                            // example: complex test plan parameters.
                                            setter(newobj);
                                        }
                                    };
                                    Serializer.Deserialize(element2, setValue, property.TypeDescriptor);
                                }
                            }