/// <summary> /// Creates a new Xml Serializer to grabs an existing one (although .NET already caches this constructor) /// </summary> /// <param name="type">The primary type of the serializer</param> /// <param name="extraTypes">The extra types to be included</param> /// <returns>The specified serializer</returns> public XmlSerializer CreateSerializer(Type type, params Type[] extraTypes) { // Generate key if (extraTypes.Length > 0 || !this.m_serializerKeys.TryGetValue(type, out var key)) { key = type.AssemblyQualifiedName; if (extraTypes != null) { key += string.Join(";", extraTypes.Select(o => o.AssemblyQualifiedName)); } } // Exists? if (!this.m_serializers.TryGetValue(key, out var serializer)) { if (!this.m_serializers.ContainsKey(key)) // Ensure that hasn't been generated since lock was acquired { if (type.GetCustomAttribute <AddDependentSerializersAttribute>() != null && extraTypes.Length == 0) { extraTypes = AppDomain.CurrentDomain.GetAllTypes() .Where(t => t.GetCustomAttribute <XmlRootAttribute>() != null && !t.IsEnum && !t.IsGenericTypeDefinition && !t.IsAbstract && !t.IsInterface) .Union(ModelSerializationBinder.GetRegisteredTypes()) .ToArray(); } else if (extraTypes.Length == 0) { extraTypes = AppDomain.CurrentDomain.GetAllTypes() .Where(t => t.GetCustomAttribute <XmlTypeAttribute>() != null) .Where(t => t.GetConstructor(Type.EmptyTypes) != null && !t.IsEnum && !t.IsGenericTypeDefinition && !t.IsAbstract && !t.IsInterface && (type.IsAssignableFrom(t) || type.GetProperties().Select(p => p.PropertyType.StripGeneric()).Any(p => !p.IsAbstract && !p.IsInterface && typeof(IdentifiedData).IsAssignableFrom(p) && p.IsAssignableFrom(t)))) .ToArray(); } serializer = new XmlSerializer(type, extraTypes); this.m_serializers.TryAdd(key, serializer); if (this.m_serializerKeys.TryGetValue(type, out var existingKey) && existingKey.Length < key.Length) // This one has more data than the existing { this.m_serializerKeys[type] = key; } else if (existingKey == null) { this.m_serializerKeys.TryAdd(type, key); // Link the key } } } return(serializer); }
/// <summary> /// Gets a serializer which can read the specified body /// </summary> public XmlSerializer GetSerializer(XmlReader bodyReader) { var retVal = this.m_serializers.Values.FirstOrDefault(o => o.CanDeserialize(bodyReader)); if (retVal == null) { var type = new ModelSerializationBinder().BindToType(String.Empty, bodyReader.LocalName); if (type == null) // Couldn't find type so attempt to do a deep resolution { var candidates = AppDomain.CurrentDomain.GetAllTypes().Where(x => x.GetCustomAttribute <XmlRootAttribute>() != null) .Select(o => new { X = o.GetCustomAttribute <XmlRootAttribute>(), T = o }) .Where(x => x.X.Namespace == bodyReader.NamespaceURI && x.X.ElementName == bodyReader.LocalName); if (!candidates.Any() || candidates.Count() > 1) { throw new InvalidOperationException($"{bodyReader.NamespaceURI}#{bodyReader.LocalName} is not understood or is ambiguous - are you missing a plugin?"); } type = candidates.Single().T; ModelSerializationBinder.RegisterModelType(type); } return(this.CreateSerializer(type)); } return(retVal); }