private static bool TryDeserializeObject(Type type, XmlReader reader, object existingInstance, out object result) { #region Local Methods to reduce complexity bool TryDeserializeKeyValue(ref TryDeserializeObjectContext ctx) { if (ctx.Type?.IsGenericTypeOf(Reflector.KeyValuePairType) != true) { return(false); } bool keyRead = false; bool valueRead = false; object key = null; object value = null; while (true) { ReadToNodeType(ctx.Reader, XmlNodeType.Element, XmlNodeType.EndElement); switch (ctx.Reader.NodeType) { case XmlNodeType.Element: switch (ctx.Reader.Name) { case nameof(KeyValuePair <_, _> .Key): if (keyRead) { Throw.ArgumentException(Res.XmlSerializationMultipleKeys); } keyRead = true; string attrType = ctx.Reader[XmlSerializer.AttributeType]; Type keyType = attrType != null?Reflector.ResolveType(attrType) : ctx.Type.GetGenericArguments()[0]; if (!TryDeserializeObject(keyType, ctx.Reader, null, out key)) { if (attrType != null && keyType == null) { Throw.ReflectionException(Res.XmlSerializationCannotResolveType(attrType)); } Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(keyType)); } break; case nameof(KeyValuePair <_, _> .Value): if (valueRead) { Throw.ArgumentException(Res.XmlSerializationMultipleValues); } valueRead = true; attrType = ctx.Reader[XmlSerializer.AttributeType]; Type valueType = attrType != null?Reflector.ResolveType(attrType) : ctx.Type.GetGenericArguments()[1]; if (!TryDeserializeObject(valueType, ctx.Reader, null, out value)) { if (attrType != null && valueType == null) { Throw.ReflectionException(Res.XmlSerializationCannotResolveType(attrType)); } Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(valueType)); } break; default: Throw.ArgumentException(Res.XmlSerializationUnexpectedElement(ctx.Reader.Name)); break; } break; case XmlNodeType.EndElement: // end of KeyValue: checking whether both key and value have been read if (!keyRead) { Throw.ArgumentException(Res.XmlSerializationKeyValueMissingKey); } if (!valueRead) { Throw.ArgumentException(Res.XmlSerializationKeyValueMissingValue); } ctx.Result = Activator.CreateInstance(ctx.Type); Accessors.SetKeyValue(ctx.Result, key, value); return(true); } } } void DeserializeBinary(ref TryDeserializeObjectContext ctx) { if (ctx.Reader.IsEmptyElement) { return; } string attrCrc = ctx.Reader[XmlSerializer.AttributeCrc]; ReadToNodeType(ctx.Reader, XmlNodeType.Text); byte[] data = Convert.FromBase64String(ctx.Reader.Value); if (attrCrc != null) { if (Crc32.CalculateHash(data).ToString("X8", CultureInfo.InvariantCulture) != attrCrc) { Throw.ArgumentException(Res.XmlSerializationCrcError); } } ctx.Result = BinarySerializer.Deserialize(data); ReadToNodeType(ctx.Reader, XmlNodeType.EndElement); } bool TryDeserializeComplexObject(ref TryDeserializeObjectContext ctx) { if (ctx.Type == null || ctx.Reader.IsEmptyElement) { return(false); } // 1.) array (both existing and new) if (ctx.Type.IsArray) { ctx.Result = DeserializeArray(ctx.ExistingInstance as Array, ctx.Type.GetElementType(), ctx.Reader, true); return(true); } // 2.) existing read-write collection if (ctx.Type.IsReadWriteCollection(ctx.ExistingInstance)) { DeserializeContent(ctx.Reader, ctx.ExistingInstance); ctx.Result = ctx.ExistingInstance; return(true); } bool isCollection = ctx.Type.IsSupportedCollectionForReflection(out var defaultCtor, out var collectionCtor, out var elementType, out bool isDictionary); // 3.) New collection by collectionCtor (only if there is no defaultCtor) if (isCollection && defaultCtor == null && !ctx.Type.IsValueType) { ctx.Result = DeserializeContentByInitializerCollection(ctx.Reader, collectionCtor, elementType, isDictionary); return(true); } ctx.Result = ctx.ExistingInstance ?? (ctx.Type.CanBeCreatedWithoutParameters() ? ctx.Type.IsValueType ? Activator.CreateInstance(ctx.Type) : CreateInstanceAccessor.GetAccessor(ctx.Type).CreateInstance() : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(ctx.Type))); // 4.) New collection by collectionCtor again (there IS defaultCtor but the new instance is read-only so falling back to collectionCtor) if (isCollection && !ctx.Type.IsReadWriteCollection(ctx.Result)) { if (collectionCtor != null) { ctx.Result = DeserializeContentByInitializerCollection(ctx.Reader, collectionCtor, elementType, isDictionary); return(true); } Throw.SerializationException(Res.XmlSerializationCannotDeserializeReadOnlyCollection(ctx.Type)); } // 5.) Newly created collection or any other object (both existing and new) DeserializeContent(ctx.Reader, ctx.Result); return(true); } #endregion // null value if (reader.IsEmptyElement && (type == null || !type.IsValueType || type.IsNullable())) { result = null; return(true); } if (type != null && type.IsNullable()) { type = Nullable.GetUnderlyingType(type); } // a.) If type can be natively parsed, parsing from string if (type != null && type.CanBeParsedNatively()) { string value = ReadStringValue(reader); result = value.Parse(type); return(true); } // b.) Deserialize IXmlSerializable string format = reader[XmlSerializer.AttributeFormat]; if (type != null && format == XmlSerializer.AttributeValueCustom) { object instance = existingInstance ?? (type.CanBeCreatedWithoutParameters() ? type.IsValueType ? Activator.CreateInstance(type) : CreateInstanceAccessor.GetAccessor(type).CreateInstance() : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(type))); if (!(instance is IXmlSerializable xmlSerializable)) { result = default; Throw.ArgumentException(Res.XmlSerializationNotAnIXmlSerializable(type)); return(default);
/// <summary> /// Serializing a collection by XmlWriter /// </summary> private void SerializeCollection(IEnumerable collection, Type elementType, bool typeNeeded, XmlWriter writer, DesignerSerializationVisibility visibility) { if (collection == null) { return; } // array collection if (collection is Array array) { if (typeNeeded) { writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(collection.GetType())); } // multidimensional or nonzero-based array if (array.Rank > 1 || array.GetLowerBound(0) != 0) { StringBuilder dim = new StringBuilder(); for (int i = 0; i < array.Rank; i++) { int low; if ((low = array.GetLowerBound(i)) != 0) { dim.Append(low + ".." + (low + array.GetLength(i) - 1)); } else { dim.Append(array.GetLength(i)); } if (i < array.Rank - 1) { dim.Append(','); } } writer.WriteAttributeString(XmlSerializer.AttributeDim, dim.ToString()); } else { writer.WriteAttributeString(XmlSerializer.AttributeLength, array.Length.ToString(CultureInfo.InvariantCulture)); } if (array.Length == 0) { // signing that collection is not null - now it will be at least <Collection></Collection> instead of <Collection /> writer.WriteString(String.Empty); return; } // array of a primitive type if (elementType.IsPrimitive && (Options & XmlSerializationOptions.CompactSerializationOfPrimitiveArrays) != XmlSerializationOptions.None) { byte[] data = new byte[Buffer.ByteLength(array)]; Buffer.BlockCopy(array, 0, data, 0, data.Length); if ((Options & XmlSerializationOptions.OmitCrcAttribute) == XmlSerializationOptions.None) { writer.WriteAttributeString(XmlSerializer.AttributeCrc, Crc32.CalculateHash(data).ToString("X8", CultureInfo.InvariantCulture)); } writer.WriteString(Convert.ToBase64String(data)); return; } // non-primitive type array or compact serialization is not enabled if (elementType.IsPointer) { Throw.NotSupportedException(Res.SerializationPointerArrayTypeNotSupported(collection.GetType())); } foreach (var item in array) { writer.WriteStartElement(XmlSerializer.ElementItem); if (item == null) { writer.WriteEndElement(); } else { SerializeObject(item, !elementType.IsSealed && item.GetType() != elementType, writer, visibility); writer.WriteFullEndElement(); } } return; } // non-array collection if (typeNeeded) { writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(collection.GetType())); } // serializing main properties first SerializeMembers(collection, writer, visibility); // serializing items foreach (var item in collection) { writer.WriteStartElement(XmlSerializer.ElementItem); if (item == null) { writer.WriteEndElement(); } else { SerializeObject(item, !elementType.IsSealed && item.GetType() != elementType, writer, visibility); writer.WriteFullEndElement(); } } }
private static bool TryDeserializeObject(Type type, XElement element, object existingInstance, out object result) { #region Local Methods to reduce complexity bool TryDeserializeKeyValue(ref TryDeserializeObjectContext ctx) { if (ctx.Type?.IsGenericTypeOf(Reflector.KeyValuePairType) == true) { // key XElement xItem = ctx.Element.Element(nameof(KeyValuePair <_, _> .Key)); if (xItem == null) { Throw.ArgumentException(Res.XmlSerializationKeyValueMissingKey); } XAttribute xType = xItem.Attribute(XmlSerializer.AttributeType); Type keyType = xType != null?Reflector.ResolveType(xType.Value) : ctx.Type.GetGenericArguments()[0]; if (!TryDeserializeObject(keyType, xItem, null, out object key)) { if (xType != null && keyType == null) { Throw.ReflectionException(Res.XmlSerializationCannotResolveType(xType.Value)); } Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(keyType)); } // value xItem = ctx.Element.Element(nameof(KeyValuePair <_, _> .Value)); if (xItem == null) { Throw.ArgumentException(Res.XmlSerializationKeyValueMissingValue); } xType = xItem.Attribute(XmlSerializer.AttributeType); Type valueType = xType != null?Reflector.ResolveType(xType.Value) : ctx.Type.GetGenericArguments()[1]; if (!TryDeserializeObject(valueType, xItem, null, out object value)) { if (xType != null && valueType == null) { Throw.ReflectionException(Res.XmlSerializationCannotResolveType(xType.Value)); } Throw.NotSupportedException(Res.XmlSerializationDeserializingTypeNotSupported(valueType)); } ctx.Result = Activator.CreateInstance(ctx.Type); Accessors.SetKeyValue(ctx.Result, key, value); return(true); } return(false); } void DeserializeBinary(ref TryDeserializeObjectContext ctx) { if (ctx.Element.IsEmpty) { return; } byte[] data = Convert.FromBase64String(ctx.Element.Value); XAttribute attrCrc = ctx.Element.Attribute(XmlSerializer.AttributeCrc); if (attrCrc != null) { if (Crc32.CalculateHash(data).ToString("X8", CultureInfo.InvariantCulture) != attrCrc.Value) { Throw.ArgumentException(Res.XmlSerializationCrcError); } } ctx.Result = BinarySerializer.Deserialize(data); } bool TryDeserializeComplexObject(ref TryDeserializeObjectContext ctx) { if (ctx.Type != null && !ctx.Element.IsEmpty) { // 1.) array (both existing and new) if (ctx.Type.IsArray) { ctx.Result = DeserializeArray(ctx.ExistingInstance as Array, ctx.Type.GetElementType(), ctx.Element, true); return(true); } // 2.) existing read-write collection if (ctx.Type.IsReadWriteCollection(ctx.ExistingInstance)) { DeserializeContent(ctx.Element, ctx.ExistingInstance); ctx.Result = ctx.ExistingInstance; return(true); } bool isCollection = ctx.Type.IsSupportedCollectionForReflection(out var defaultCtor, out var collectionCtor, out var elementType, out bool isDictionary); // 3.) New collection by collectionCtor (only if there is no defaultCtor) if (isCollection && defaultCtor == null && !ctx.Type.IsValueType) { ctx.Result = DeserializeContentByInitializerCollection(ctx.Element, collectionCtor, elementType, isDictionary); return(true); } ctx.Result = ctx.ExistingInstance ?? (ctx.Type.CanBeCreatedWithoutParameters() ? ctx.Type.IsValueType ? Activator.CreateInstance(ctx.Type) : CreateInstanceAccessor.GetAccessor(ctx.Type).CreateInstance() : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(ctx.Type))); // 4.) New collection by collectionCtor again (there IS defaultCtor but the new instance is read-only so falling back to collectionCtor) if (isCollection && !ctx.Type.IsReadWriteCollection(ctx.Result)) { if (collectionCtor != null) { ctx.Result = DeserializeContentByInitializerCollection(ctx.Element, collectionCtor, elementType, isDictionary); return(true); } Throw.SerializationException(Res.XmlSerializationCannotDeserializeReadOnlyCollection(ctx.Type)); } // 5.) Newly created collection or any other object (both existing and new) DeserializeContent(ctx.Element, ctx.Result); return(true); } return(false); } #endregion // null value if (element.IsEmpty && (type == null || !type.IsValueType || type.IsNullable())) { result = null; return(true); } if (type != null && type.IsNullable()) { type = Nullable.GetUnderlyingType(type); } // a.) If type can be natively parsed, parsing from string if (type != null && type.CanBeParsedNatively()) { string value = ReadStringValue(element); result = value.Parse(type); return(true); } // b.) Deserialize IXmlSerializable string format = element.Attribute(XmlSerializer.AttributeFormat)?.Value; if (type != null && format == XmlSerializer.AttributeValueCustom) { object instance = existingInstance ?? (type.CanBeCreatedWithoutParameters() ? type.IsValueType ? Activator.CreateInstance(type) : CreateInstanceAccessor.GetAccessor(type).CreateInstance() : Throw.ReflectionException <object>(Res.XmlSerializationNoDefaultCtor(type))); if (!(instance is IXmlSerializable xmlSerializable)) { result = default; Throw.ArgumentException(Res.XmlSerializationNotAnIXmlSerializable(type)); return(default);