|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization.DataContracts;
using System.Security;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, System.Runtime.Serialization.DataContracts.DataContract>;
namespace System.Runtime.Serialization
{
internal class XmlObjectSerializerReadContext : XmlObjectSerializerContext
{
internal Attributes? attributes;
private HybridObjectCache? _deserializedObjects;
private XmlSerializableReader? _xmlSerializableReader;
private XmlDocument? _xmlDocument;
private Attributes? _attributesInXmlData;
private XmlReaderDelegator? _extensionDataReader;
private object? _getOnlyCollectionValue;
private bool _isGetOnlyCollection;
private HybridObjectCache DeserializedObjects => _deserializedObjects ??= new HybridObjectCache();
private XmlDocument Document => _xmlDocument ??= new XmlDocument();
internal override bool IsGetOnlyCollection
{
get { return _isGetOnlyCollection; }
set { _isGetOnlyCollection = value; }
}
internal object? GetCollectionMember()
{
return _getOnlyCollectionValue;
}
internal void StoreCollectionMemberInfo(object? collectionMember)
{
_getOnlyCollectionValue = collectionMember;
_isGetOnlyCollection = true;
}
internal void ResetCollectionMemberInfo()
{
_getOnlyCollectionValue = null;
_isGetOnlyCollection = false;
}
[DoesNotReturn]
internal static void ThrowNullValueReturnedForGetOnlyCollectionException(Type type)
{
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.NullValueReturnedForGetOnlyCollection, DataContract.GetClrTypeFullName(type)));
}
[DoesNotReturn]
internal static void ThrowArrayExceededSizeException(int arraySize, Type type)
{
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArrayExceededSize, arraySize, DataContract.GetClrTypeFullName(type)));
}
internal static XmlObjectSerializerReadContext CreateContext(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver)
{
return (serializer.PreserveObjectReferences || serializer.SerializationSurrogateProvider != null)
? new XmlObjectSerializerReadContextComplex(serializer, rootTypeDataContract, dataContractResolver)
: new XmlObjectSerializerReadContext(serializer, rootTypeDataContract, dataContractResolver);
}
internal XmlObjectSerializerReadContext(XmlObjectSerializer serializer, int maxItemsInObjectGraph, StreamingContext streamingContext, bool ignoreExtensionDataObject)
: base(serializer, maxItemsInObjectGraph, streamingContext, ignoreExtensionDataObject)
{
}
internal XmlObjectSerializerReadContext(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver? dataContractResolver)
: base(serializer, rootTypeDataContract, dataContractResolver)
{
this.attributes = new Attributes();
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, int id, RuntimeTypeHandle declaredTypeHandle, string name, string ns)
{
DataContract dataContract = GetDataContract(id, declaredTypeHandle);
return InternalDeserialize(xmlReader, name, ns, Type.GetTypeFromHandle(declaredTypeHandle)!, ref dataContract);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string? name, string? ns)
{
DataContract dataContract = GetDataContract(declaredType);
return InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal virtual object? InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract? dataContract, string? name, string? ns)
{
dataContract ??= GetDataContract(declaredType);
return InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
protected bool TryHandleNullOrRef(XmlReaderDelegator reader, string? name, string? ns, Type declaredType, ref object? retObj)
{
ReadAttributes(reader);
if (attributes.Ref != Globals.NewObjectId)
{
if (_isGetOnlyCollection)
{
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ErrorDeserializing, SR.Format(SR.ErrorTypeInfo, DataContract.GetClrTypeFullName(declaredType)), SR.Format(SR.XmlStartElementExpected, Globals.RefLocalName)));
}
else
{
retObj = GetExistingObject(attributes.Ref, declaredType, name, ns);
reader.Skip();
return true;
}
}
else if (attributes.XsiNil)
{
reader.Skip();
return true;
}
return false;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
protected object? InternalDeserialize(XmlReaderDelegator reader, string? name, string? ns, Type declaredType, ref DataContract dataContract)
{
object? retObj = null;
if (TryHandleNullOrRef(reader, name, ns, dataContract.UnderlyingType, ref retObj))
return retObj;
bool knownTypesAddedInCurrentScope = false;
if (dataContract.KnownDataContracts?.Count > 0)
{
scopedKnownTypes.Push(dataContract.KnownDataContracts);
knownTypesAddedInCurrentScope = true;
}
Debug.Assert(attributes != null);
if (attributes.XsiTypeName != null)
{
DataContract? tempDataContract = ResolveDataContractFromKnownTypes(attributes.XsiTypeName, attributes.XsiTypeNamespace, dataContract, declaredType);
if (tempDataContract == null)
{
if (DataContractResolver == null)
{
throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(reader, SR.Format(SR.DcTypeNotFoundOnDeserialize, attributes.XsiTypeNamespace, attributes.XsiTypeName, reader.NamespaceURI, reader.LocalName)));
}
throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(reader, SR.Format(SR.DcTypeNotResolvedOnDeserialize, attributes.XsiTypeNamespace, attributes.XsiTypeName, reader.NamespaceURI, reader.LocalName)));
}
dataContract = tempDataContract;
knownTypesAddedInCurrentScope = ReplaceScopedKnownTypesTop(dataContract.KnownDataContracts, knownTypesAddedInCurrentScope);
}
if (dataContract.IsISerializable && attributes.FactoryTypeName != null)
{
DataContract? factoryDataContract = ResolveDataContractFromKnownTypes(attributes.FactoryTypeName, attributes.FactoryTypeNamespace, dataContract, declaredType);
if (factoryDataContract != null)
{
if (factoryDataContract.IsISerializable)
{
dataContract = factoryDataContract;
knownTypesAddedInCurrentScope = ReplaceScopedKnownTypesTop(dataContract.KnownDataContracts, knownTypesAddedInCurrentScope);
}
else
{
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.FactoryTypeNotISerializable, DataContract.GetClrTypeFullName(factoryDataContract.UnderlyingType), DataContract.GetClrTypeFullName(dataContract.UnderlyingType)));
}
}
}
if (knownTypesAddedInCurrentScope)
{
object? obj = ReadDataContractValue(dataContract, reader);
scopedKnownTypes.Pop();
return obj;
}
else
{
return ReadDataContractValue(dataContract, reader);
}
}
private bool ReplaceScopedKnownTypesTop(DataContractDictionary? knownDataContracts, bool knownTypesAddedInCurrentScope)
{
if (knownTypesAddedInCurrentScope)
{
scopedKnownTypes.Pop();
knownTypesAddedInCurrentScope = false;
}
if (knownDataContracts?.Count > 0)
{
scopedKnownTypes.Push(knownDataContracts);
knownTypesAddedInCurrentScope = true;
}
return knownTypesAddedInCurrentScope;
}
internal static bool MoveToNextElement(XmlReaderDelegator xmlReader)
{
return (xmlReader.MoveToContent() != XmlNodeType.EndElement);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal int GetMemberIndex(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, int memberIndex, ExtensionDataObject? extensionData)
{
for (int i = memberIndex + 1; i < memberNames.Length; i++)
{
if (xmlReader.IsStartElement(memberNames[i], memberNamespaces[i]))
return i;
}
HandleMemberNotFound(xmlReader, extensionData, memberIndex);
return memberNames.Length;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal int GetMemberIndexWithRequiredMembers(XmlReaderDelegator xmlReader, XmlDictionaryString[] memberNames, XmlDictionaryString[] memberNamespaces, int memberIndex, int requiredIndex, ExtensionDataObject? extensionData)
{
for (int i = memberIndex + 1; i < memberNames.Length; i++)
{
if (xmlReader.IsStartElement(memberNames[i], memberNamespaces[i]))
{
if (requiredIndex < i)
ThrowRequiredMemberMissingException(xmlReader, memberIndex, requiredIndex, memberNames);
return i;
}
}
HandleMemberNotFound(xmlReader, extensionData, memberIndex);
return memberNames.Length;
}
[DoesNotReturn]
internal static void ThrowRequiredMemberMissingException(XmlReaderDelegator xmlReader, int memberIndex, int requiredIndex, XmlDictionaryString[] memberNames)
{
StringBuilder stringBuilder = new StringBuilder();
if (requiredIndex == memberNames.Length)
requiredIndex--;
for (int i = memberIndex + 1; i <= requiredIndex; i++)
{
if (stringBuilder.Length != 0)
stringBuilder.Append(" | ");
stringBuilder.Append(memberNames[i].Value);
}
throw XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.Format(SR.UnexpectedElementExpectingElements, xmlReader.NodeType, xmlReader.LocalName, xmlReader.NamespaceURI, stringBuilder.ToString())));
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
protected void HandleMemberNotFound(XmlReaderDelegator xmlReader, ExtensionDataObject? extensionData, int memberIndex)
{
xmlReader.MoveToContent();
if (xmlReader.NodeType != XmlNodeType.Element)
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
if (IgnoreExtensionDataObject || extensionData == null)
SkipUnknownElement(xmlReader);
else
HandleUnknownElement(xmlReader, extensionData, memberIndex);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal void HandleUnknownElement(XmlReaderDelegator xmlReader, ExtensionDataObject extensionData, int memberIndex)
{
extensionData.Members ??= new List<ExtensionDataMember>();
extensionData.Members.Add(ReadExtensionDataMember(xmlReader, memberIndex));
}
internal void SkipUnknownElement(XmlReaderDelegator xmlReader)
{
ReadAttributes(xmlReader);
xmlReader.Skip();
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal string ReadIfNullOrRef(XmlReaderDelegator xmlReader, Type memberType, bool isMemberTypeSerializable)
{
Debug.Assert(attributes != null);
if (attributes.Ref != Globals.NewObjectId)
{
CheckIfTypeSerializable(memberType, isMemberTypeSerializable);
xmlReader.Skip();
return attributes.Ref;
}
else if (attributes.XsiNil)
{
CheckIfTypeSerializable(memberType, isMemberTypeSerializable);
xmlReader.Skip();
return Globals.NullObjectId;
}
return Globals.NewObjectId;
}
[MemberNotNull(nameof(attributes))]
internal virtual void ReadAttributes(XmlReaderDelegator xmlReader)
{
attributes ??= new Attributes();
attributes.Read(xmlReader);
}
internal void ResetAttributes()
{
attributes?.Reset();
}
internal string GetObjectId()
{
Debug.Assert(attributes != null);
return attributes.Id;
}
internal virtual int GetArraySize()
{
return -1;
}
internal void AddNewObject(object? obj)
{
Debug.Assert(attributes != null);
AddNewObjectWithId(attributes.Id, obj);
}
internal void AddNewObjectWithId(string id, object? obj)
{
if (id != Globals.NewObjectId)
DeserializedObjects.Add(id, obj);
if (_extensionDataReader?.UnderlyingExtensionDataReader != null)
_extensionDataReader.UnderlyingExtensionDataReader.SetDeserializedValue(obj);
}
public void ReplaceDeserializedObject(string id, object? oldObj, object? newObj)
{
if (object.ReferenceEquals(oldObj, newObj))
return;
if (id != Globals.NewObjectId)
{
// In certain cases (IObjectReference, SerializationSurrogate or DataContractSurrogate),
// an object can be replaced with a different object once it is deserialized. If the
// object happens to be referenced from within itself, that reference needs to be updated
// with the new instance. BinaryFormatter supports this by fixing up such references later.
// These XmlObjectSerializer implementations do not currently support fix-ups. Hence we
// throw in such cases to allow us add fix-up support in the future if we need to.
if (DeserializedObjects.IsObjectReferenced(id))
{
string oldType = (oldObj != null) ? DataContract.GetClrTypeFullName(oldObj.GetType()) : SR.UnknownNullType;
string newType = (newObj != null) ? DataContract.GetClrTypeFullName(newObj.GetType()) : SR.UnknownNullType;
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.FactoryObjectContainsSelfReference, oldType, newType, id));
}
DeserializedObjects.Remove(id);
DeserializedObjects.Add(id, newObj);
}
if (_extensionDataReader?.UnderlyingExtensionDataReader != null)
_extensionDataReader.UnderlyingExtensionDataReader.SetDeserializedValue(newObj);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal object? GetExistingObject(string id, Type? type, string? name, string? ns)
{
object? retObj = DeserializedObjects.GetObject(id);
if (retObj == null)
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DeserializedObjectWithIdNotFound, id));
if (retObj is IDataNode dataNode)
{
retObj = (dataNode.Value != null && dataNode.IsFinalValue) ? dataNode.Value : DeserializeFromExtensionData(dataNode, type ?? dataNode.DataType, name, ns);
}
return retObj;
}
private object GetExistingObjectOrExtensionData(string id)
{
object? retObj = DeserializedObjects.GetObject(id);
if (retObj == null)
{
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.DeserializedObjectWithIdNotFound, id));
}
return retObj;
}
#pragma warning disable SYSLIB0050 // IObjectReference is obsolete
public object GetRealObject(IObjectReference obj, string id)
{
object? realObj = obj.GetRealObject(this.GetStreamingContext());
// If GetRealObject returns null, it indicates that the object could not resolve itself because
// it is missing information. This may occur in a case where multiple IObjectReference instances
// depend on each other. BinaryFormatter supports this by fixing up the references later. These
// XmlObjectSerializer implementations do not support fix-ups since the format does not contain
// forward references. However, we throw for this case since it allows us to add fix-up support
// in the future if we need to.
if (realObj == null)
throw XmlObjectSerializer.CreateSerializationException("error");
ReplaceDeserializedObject(id, obj, realObj);
return realObj;
}
#pragma warning restore SYSLIB0050
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private object? DeserializeFromExtensionData(IDataNode dataNode, Type type, string? name, string? ns)
{
// _extensionDataRead is only ever created here, so we know the casting property 'UnderlyingExtensionDataReader' won't be null.
_extensionDataReader ??= CreateReaderDelegatorForReader(new ExtensionDataReader(this));
_extensionDataReader.UnderlyingExtensionDataReader!.SetDataNode(dataNode, name, ns);
object? retObj = InternalDeserialize(_extensionDataReader, type, name, ns);
dataNode.Clear();
_extensionDataReader.UnderlyingExtensionDataReader!.Reset();
return retObj;
}
internal static void Read(XmlReaderDelegator xmlReader)
{
if (!xmlReader.Read())
throw XmlObjectSerializer.CreateSerializationException(SR.UnexpectedEndOfFile);
}
internal static void ParseQualifiedName(string qname, XmlReaderDelegator xmlReader, out string name, out string? ns, out string prefix)
{
int colon = qname.IndexOf(':');
prefix = "";
if (colon >= 0)
prefix = qname.Substring(0, colon);
name = qname.Substring(colon + 1);
ns = xmlReader.LookupNamespace(prefix);
}
internal static T[] EnsureArraySize<T>(T[] array, int index)
{
if (array.Length <= index)
{
if (index == int.MaxValue)
{
throw XmlObjectSerializer.CreateSerializationException(
SR.Format(SR.MaxArrayLengthExceeded, int.MaxValue,
DataContract.GetClrTypeFullName(typeof(T))));
}
int newSize = (index < int.MaxValue / 2) ? index * 2 : int.MaxValue;
T[] newArray = new T[newSize];
Array.Copy(array, newArray, array.Length);
array = newArray;
}
return array;
}
internal static T[] TrimArraySize<T>(T[] array, int size)
{
if (size != array.Length)
{
T[] newArray = new T[size];
Array.Copy(array, newArray, size);
array = newArray;
}
return array;
}
internal void CheckEndOfArray(XmlReaderDelegator xmlReader, int arraySize, XmlDictionaryString itemName, XmlDictionaryString itemNamespace)
{
if (xmlReader.NodeType == XmlNodeType.EndElement)
return;
while (xmlReader.IsStartElement())
{
if (xmlReader.IsStartElement(itemName, itemNamespace))
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArrayExceededSizeAttribute, arraySize, itemName.Value, itemNamespace.Value));
SkipUnknownElement(xmlReader);
}
if (xmlReader.NodeType != XmlNodeType.EndElement)
throw CreateUnexpectedStateException(XmlNodeType.EndElement, xmlReader);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal object? ReadIXmlSerializable(XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType)
{
_xmlSerializableReader ??= new XmlSerializableReader();
return ReadIXmlSerializable(_xmlSerializableReader, xmlReader, xmlDataContract, isMemberType);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal static object? ReadRootIXmlSerializable(XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType)
{
return ReadIXmlSerializable(new XmlSerializableReader(), xmlReader, xmlDataContract, isMemberType);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
internal static object? ReadIXmlSerializable(XmlSerializableReader xmlSerializableReader, XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, bool isMemberType)
{
object? obj = null;
xmlSerializableReader.BeginRead(xmlReader);
if (isMemberType && !xmlDataContract.HasRoot)
{
xmlReader.Read();
xmlReader.MoveToContent();
}
if (xmlDataContract.UnderlyingType == Globals.TypeOfXmlElement)
{
if (!xmlReader.IsStartElement())
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
XmlDocument xmlDoc = new XmlDocument();
obj = (XmlElement?)xmlDoc.ReadNode(xmlSerializableReader);
}
else if (xmlDataContract.UnderlyingType == Globals.TypeOfXmlNodeArray)
{
obj = XmlSerializableServices.ReadNodes(xmlSerializableReader);
}
else
{
IXmlSerializable xmlSerializable = xmlDataContract.CreateXmlSerializableDelegate();
xmlSerializable.ReadXml(xmlSerializableReader);
obj = xmlSerializable;
}
xmlSerializableReader.EndRead();
return obj;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
public SerializationInfo ReadSerializationInfo(XmlReaderDelegator xmlReader, Type type)
{
#pragma warning disable SYSLIB0050 // SerializationInfo ctor is obsolete
var serInfo = new SerializationInfo(type, XmlObjectSerializer.FormatterConverter);
#pragma warning restore SYSLIB0050
XmlNodeType nodeType;
while ((nodeType = xmlReader.MoveToContent()) != XmlNodeType.EndElement)
{
if (nodeType != XmlNodeType.Element)
{
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
}
if (xmlReader.NamespaceURI.Length != 0)
{
SkipUnknownElement(xmlReader);
continue;
}
string name = XmlConvert.DecodeName(xmlReader.LocalName);
IncrementItemCount(1);
ReadAttributes(xmlReader);
object? value;
if (attributes.Ref != Globals.NewObjectId)
{
xmlReader.Skip();
value = GetExistingObject(attributes.Ref, null, name, string.Empty);
}
else if (attributes.XsiNil)
{
xmlReader.Skip();
value = null;
}
else
{
value = InternalDeserialize(xmlReader, Globals.TypeOfObject, name, string.Empty);
}
serInfo.AddValue(name, value);
}
return serInfo;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
protected virtual DataContract? ResolveDataContractFromTypeName()
{
Debug.Assert(attributes != null);
return (attributes.XsiTypeName == null) ? null : ResolveDataContractFromKnownTypes(attributes.XsiTypeName, attributes.XsiTypeNamespace, null /*memberTypeContract*/, null);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private ExtensionDataMember ReadExtensionDataMember(XmlReaderDelegator xmlReader, int memberIndex)
{
var member = new ExtensionDataMember(xmlReader.LocalName, xmlReader.NamespaceURI)
{
MemberIndex = memberIndex
};
member.Value = xmlReader.UnderlyingExtensionDataReader != null ? xmlReader.UnderlyingExtensionDataReader.GetCurrentNode() : ReadExtensionDataValue(xmlReader);
return member;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
public IDataNode? ReadExtensionDataValue(XmlReaderDelegator xmlReader)
{
ReadAttributes(xmlReader);
IncrementItemCount(1);
IDataNode? dataNode = null;
if (attributes.Ref != Globals.NewObjectId)
{
xmlReader.Skip();
object o = GetExistingObjectOrExtensionData(attributes.Ref);
dataNode = (o is IDataNode) ? (IDataNode)o : new DataNode<object>(o);
dataNode.Id = attributes.Ref;
}
else if (attributes.XsiNil)
{
xmlReader.Skip();
dataNode = null;
}
else
{
string? dataContractName = null;
string? dataContractNamespace = null;
if (attributes.XsiTypeName != null)
{
dataContractName = attributes.XsiTypeName;
dataContractNamespace = attributes.XsiTypeNamespace;
}
if (IsReadingCollectionExtensionData(xmlReader))
{
Read(xmlReader);
dataNode = ReadUnknownCollectionData(xmlReader, dataContractName, dataContractNamespace);
}
else if (attributes.FactoryTypeName != null)
{
Read(xmlReader);
dataNode = ReadUnknownISerializableData(xmlReader, dataContractName, dataContractNamespace);
}
else if (IsReadingClassExtensionData(xmlReader))
{
Read(xmlReader);
dataNode = ReadUnknownClassData(xmlReader, dataContractName, dataContractNamespace);
}
else
{
DataContract? dataContract = ResolveDataContractFromTypeName();
if (dataContract == null)
dataNode = ReadExtensionDataValue(xmlReader, dataContractName, dataContractNamespace);
else if (dataContract is XmlDataContract)
dataNode = ReadUnknownXmlData(xmlReader, dataContractName, dataContractNamespace);
else
{
if (dataContract.IsISerializable)
{
Read(xmlReader);
dataNode = ReadUnknownISerializableData(xmlReader, dataContractName, dataContractNamespace);
}
else if (dataContract is PrimitiveDataContract)
{
if (attributes.Id == Globals.NewObjectId)
{
Read(xmlReader);
xmlReader.MoveToContent();
dataNode = ReadUnknownPrimitiveData(xmlReader, dataContract.UnderlyingType, dataContractName, dataContractNamespace);
xmlReader.ReadEndElement();
}
else
{
dataNode = new DataNode<object>(xmlReader.ReadElementContentAsAnyType(dataContract.UnderlyingType));
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
}
}
else if (dataContract is EnumDataContract)
{
dataNode = new DataNode<object>(((EnumDataContract)dataContract).ReadEnumValue(xmlReader));
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
}
else if (dataContract is ClassDataContract)
{
Read(xmlReader);
dataNode = ReadUnknownClassData(xmlReader, dataContractName, dataContractNamespace);
}
else if (dataContract is CollectionDataContract)
{
Read(xmlReader);
dataNode = ReadUnknownCollectionData(xmlReader, dataContractName, dataContractNamespace);
}
}
}
}
return dataNode;
}
protected virtual void StartReadExtensionDataValue(XmlReaderDelegator xmlReader)
{
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private IDataNode ReadExtensionDataValue(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace)
{
Debug.Assert(attributes != null);
StartReadExtensionDataValue(xmlReader);
if (attributes.UnrecognizedAttributesFound)
return ReadUnknownXmlData(xmlReader, dataContractName, dataContractNamespace);
IDictionary<string, string>? namespacesInScope = xmlReader.GetNamespacesInScope(XmlNamespaceScope.ExcludeXml);
Read(xmlReader);
xmlReader.MoveToContent();
switch (xmlReader.NodeType)
{
case XmlNodeType.Text:
return ReadPrimitiveExtensionDataValue(xmlReader, dataContractName, dataContractNamespace);
case XmlNodeType.Element:
if (xmlReader.NamespaceURI.StartsWith(Globals.DataContractXsdBaseNamespace, StringComparison.Ordinal))
return ReadUnknownClassData(xmlReader, dataContractName, dataContractNamespace);
else
return ReadAndResolveUnknownXmlData(xmlReader, namespacesInScope, dataContractName, dataContractNamespace);
case XmlNodeType.EndElement:
{
// NOTE: cannot distinguish between empty class or IXmlSerializable and typeof(object)
IDataNode objNode = ReadUnknownPrimitiveData(xmlReader, Globals.TypeOfObject, dataContractName, dataContractNamespace);
xmlReader.ReadEndElement();
objNode.IsFinalValue = false;
return objNode;
}
default:
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
}
}
protected virtual IDataNode ReadPrimitiveExtensionDataValue(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace)
{
Type valueType = xmlReader.ValueType;
if (valueType == Globals.TypeOfString)
{
// NOTE: cannot distinguish other primitives from string (default XmlReader ValueType)
IDataNode stringNode = new DataNode<object>(xmlReader.ReadContentAsString());
InitializeExtensionDataNode(stringNode, dataContractName, dataContractNamespace);
stringNode.IsFinalValue = false;
xmlReader.ReadEndElement();
return stringNode;
}
IDataNode objNode = ReadUnknownPrimitiveData(xmlReader, valueType, dataContractName, dataContractNamespace);
xmlReader.ReadEndElement();
return objNode;
}
protected void InitializeExtensionDataNode(IDataNode dataNode, string? dataContractName, string? dataContractNamespace)
{
Debug.Assert(attributes != null);
dataNode.DataContractName = dataContractName;
dataNode.DataContractNamespace = dataContractNamespace;
dataNode.ClrAssemblyName = attributes.ClrAssembly;
dataNode.ClrTypeName = attributes.ClrType;
AddNewObject(dataNode);
dataNode.Id = attributes.Id;
}
private IDataNode ReadUnknownPrimitiveData(XmlReaderDelegator xmlReader, Type type, string? dataContractName, string? dataContractNamespace)
{
IDataNode dataNode = xmlReader.ReadExtensionData(type);
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
return dataNode;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private ClassDataNode ReadUnknownClassData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace)
{
var dataNode = new ClassDataNode();
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
int memberIndex = 0;
XmlNodeType nodeType;
while ((nodeType = xmlReader.MoveToContent()) != XmlNodeType.EndElement)
{
if (nodeType != XmlNodeType.Element)
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
dataNode.Members ??= new List<ExtensionDataMember>();
dataNode.Members.Add(ReadExtensionDataMember(xmlReader, memberIndex++));
}
xmlReader.ReadEndElement();
return dataNode;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private CollectionDataNode ReadUnknownCollectionData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace)
{
Debug.Assert(attributes != null);
var dataNode = new CollectionDataNode();
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
int arraySize = attributes.ArraySZSize;
XmlNodeType nodeType;
while ((nodeType = xmlReader.MoveToContent()) != XmlNodeType.EndElement)
{
if (nodeType != XmlNodeType.Element)
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
if (dataNode.ItemName == null)
{
dataNode.ItemName = xmlReader.LocalName;
dataNode.ItemNamespace = xmlReader.NamespaceURI;
}
if (xmlReader.IsStartElement(dataNode.ItemName, dataNode.ItemNamespace!))
{
dataNode.Items ??= new List<IDataNode?>();
dataNode.Items.Add(ReadExtensionDataValue(xmlReader));
}
else
SkipUnknownElement(xmlReader);
}
xmlReader.ReadEndElement();
if (arraySize != -1)
{
dataNode.Size = arraySize;
if (dataNode.Items == null)
{
if (dataNode.Size > 0)
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArraySizeAttributeIncorrect, arraySize, 0));
}
else if (dataNode.Size != dataNode.Items.Count)
throw XmlObjectSerializer.CreateSerializationException(SR.Format(SR.ArraySizeAttributeIncorrect, arraySize, dataNode.Items.Count));
}
else
{
if (dataNode.Items != null)
{
dataNode.Size = dataNode.Items.Count;
}
else
{
dataNode.Size = 0;
}
}
return dataNode;
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private ISerializableDataNode ReadUnknownISerializableData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace)
{
Debug.Assert(attributes != null);
var dataNode = new ISerializableDataNode();
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
dataNode.FactoryTypeName = attributes.FactoryTypeName;
dataNode.FactoryTypeNamespace = attributes.FactoryTypeNamespace;
XmlNodeType nodeType;
while ((nodeType = xmlReader.MoveToContent()) != XmlNodeType.EndElement)
{
if (nodeType != XmlNodeType.Element)
throw CreateUnexpectedStateException(XmlNodeType.Element, xmlReader);
if (xmlReader.NamespaceURI.Length != 0)
{
SkipUnknownElement(xmlReader);
continue;
}
var member = new ISerializableDataMember(xmlReader.LocalName);
member.Value = ReadExtensionDataValue(xmlReader);
dataNode.Members ??= new List<ISerializableDataMember>();
dataNode.Members.Add(member);
}
xmlReader.ReadEndElement();
return dataNode;
}
private XmlDataNode ReadUnknownXmlData(XmlReaderDelegator xmlReader, string? dataContractName, string? dataContractNamespace)
{
XmlDataNode dataNode = new XmlDataNode()
{
OwnerDocument = Document
};
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
if (xmlReader.NodeType == XmlNodeType.EndElement)
return dataNode;
List<XmlAttribute>? xmlAttributes = null;
List<XmlNode>? xmlChildNodes = null;
XmlNodeType nodeType = xmlReader.MoveToContent();
if (nodeType != XmlNodeType.Text)
{
while (xmlReader.MoveToNextAttribute())
{
string ns = xmlReader.NamespaceURI;
if (ns != Globals.SerializationNamespace && ns != Globals.SchemaInstanceNamespace)
{
xmlAttributes ??= new List<XmlAttribute>();
xmlAttributes.Add((XmlAttribute)Document.ReadNode(xmlReader.UnderlyingReader)!);
}
}
Read(xmlReader);
}
while (xmlReader.MoveToContent() != XmlNodeType.EndElement)
{
if (xmlReader.EOF)
throw XmlObjectSerializer.CreateSerializationException(SR.UnexpectedEndOfFile);
xmlChildNodes ??= new List<XmlNode>();
xmlChildNodes.Add(Document.ReadNode(xmlReader.UnderlyingReader)!);
}
xmlReader.ReadEndElement();
dataNode.XmlAttributes = xmlAttributes;
dataNode.XmlChildNodes = xmlChildNodes;
return dataNode;
}
// Pattern-recognition logic: the method reads XML elements into DOM. To recognize as an array, it requires that
// all items have the same name and namespace. To recognize as an ISerializable type, it requires that all
// items be unqualified. If the XML only contains elements (no attributes or other nodes) is recognized as a
// class/class hierarchy. Otherwise it is deserialized as XML.
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
private IDataNode ReadAndResolveUnknownXmlData(XmlReaderDelegator xmlReader, IDictionary<string, string>? namespaces,
string? dataContractName, string? dataContractNamespace)
{
bool couldBeISerializableData = true;
bool couldBeCollectionData = true;
bool couldBeClassData = true;
string? elementNs = null, elementName = null;
var xmlChildNodes = new List<XmlNode>();
List<XmlAttribute>? xmlAttributes = null;
if (namespaces != null)
{
xmlAttributes = new List<XmlAttribute>();
foreach (KeyValuePair<string, string> prefixNsPair in namespaces)
{
xmlAttributes.Add(AddNamespaceDeclaration(prefixNsPair.Key, prefixNsPair.Value));
}
}
XmlNodeType nodeType;
while ((nodeType = xmlReader.NodeType) != XmlNodeType.EndElement)
{
if (nodeType == XmlNodeType.Element)
{
string ns = xmlReader.NamespaceURI;
string name = xmlReader.LocalName;
if (couldBeISerializableData)
couldBeISerializableData = (ns.Length == 0);
if (couldBeCollectionData)
{
if (elementName == null)
{
elementName = name;
elementNs = ns;
}
else
{
couldBeCollectionData = elementName == name && elementNs == ns;
}
}
}
else if (xmlReader.EOF)
throw XmlObjectSerializer.CreateSerializationException(SR.UnexpectedEndOfFile);
else if (IsContentNode(xmlReader.NodeType))
couldBeClassData = couldBeISerializableData = couldBeCollectionData = false;
_attributesInXmlData ??= new Attributes();
_attributesInXmlData.Read(xmlReader);
XmlNode childNode = Document.ReadNode(xmlReader.UnderlyingReader)!;
xmlChildNodes.Add(childNode);
if (namespaces == null)
{
if (_attributesInXmlData.XsiTypeName != null)
childNode.Attributes!.Append(AddNamespaceDeclaration(_attributesInXmlData.XsiTypePrefix, _attributesInXmlData.XsiTypeNamespace));
if (_attributesInXmlData.FactoryTypeName != null)
childNode.Attributes!.Append(AddNamespaceDeclaration(_attributesInXmlData.FactoryTypePrefix, _attributesInXmlData.FactoryTypeNamespace));
}
}
xmlReader.ReadEndElement();
if (elementName != null && couldBeCollectionData)
return ReadUnknownCollectionData(CreateReaderOverChildNodes(xmlAttributes, xmlChildNodes), dataContractName, dataContractNamespace);
else if (couldBeISerializableData)
return ReadUnknownISerializableData(CreateReaderOverChildNodes(xmlAttributes, xmlChildNodes), dataContractName, dataContractNamespace);
else if (couldBeClassData)
return ReadUnknownClassData(CreateReaderOverChildNodes(xmlAttributes, xmlChildNodes), dataContractName, dataContractNamespace);
else
{
XmlDataNode dataNode = new XmlDataNode()
{
OwnerDocument = Document
};
InitializeExtensionDataNode(dataNode, dataContractName, dataContractNamespace);
dataNode.XmlChildNodes = xmlChildNodes;
dataNode.XmlAttributes = xmlAttributes;
return dataNode;
}
}
private static bool IsContentNode(XmlNodeType nodeType)
{
switch (nodeType)
{
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.Comment:
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.DocumentType:
return false;
default:
return true;
}
}
internal XmlReaderDelegator CreateReaderOverChildNodes(IList<XmlAttribute>? xmlAttributes, IList<XmlNode> xmlChildNodes)
{
XmlElement wrapperElement = CreateWrapperXmlElement(Document, xmlAttributes, xmlChildNodes, null, null, null);
XmlReaderDelegator nodeReader = CreateReaderDelegatorForReader(new XmlNodeReader(wrapperElement));
nodeReader.MoveToContent();
Read(nodeReader);
return nodeReader;
}
internal static XmlElement CreateWrapperXmlElement(XmlDocument document, IList<XmlAttribute>? xmlAttributes, IList<XmlNode>? xmlChildNodes, string? prefix, string? localName, string? ns)
{
localName ??= "wrapper";
ns ??= string.Empty;
XmlElement wrapperElement = document.CreateElement(prefix, localName, ns);
if (xmlAttributes != null)
{
for (int i = 0; i < xmlAttributes.Count; i++)
{
wrapperElement.Attributes.Append((XmlAttribute)xmlAttributes[i]);
}
}
if (xmlChildNodes != null)
{
for (int i = 0; i < xmlChildNodes.Count; i++)
{
wrapperElement.AppendChild(xmlChildNodes[i]);
}
}
return wrapperElement;
}
private XmlAttribute AddNamespaceDeclaration(string? prefix, string? ns)
{
XmlAttribute attribute = string.IsNullOrEmpty(prefix) ?
Document.CreateAttribute(null, Globals.XmlnsPrefix, Globals.XmlnsNamespace) :
Document.CreateAttribute(Globals.XmlnsPrefix, prefix, Globals.XmlnsNamespace);
attribute.Value = ns;
return attribute;
}
internal static Exception CreateUnexpectedStateException(XmlNodeType expectedState, XmlReaderDelegator xmlReader)
{
return XmlObjectSerializer.CreateSerializationExceptionWithReaderDetails(SR.Format(SR.ExpectingState, expectedState), xmlReader);
}
[RequiresDynamicCode(DataContract.SerializerAOTWarning)]
[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
protected virtual object? ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
{
return dataContract.ReadXmlValue(reader, this);
}
protected virtual XmlReaderDelegator CreateReaderDelegatorForReader(XmlReader xmlReader)
{
return new XmlReaderDelegator(xmlReader);
}
protected virtual bool IsReadingCollectionExtensionData(XmlReaderDelegator xmlReader)
{
Debug.Assert(attributes != null);
return (attributes.ArraySZSize != -1);
}
protected virtual bool IsReadingClassExtensionData(XmlReaderDelegator xmlReader)
{
return false;
}
}
}
|