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);