371 lines
14 KiB
C#
371 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Security;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
using System.Xml.Serialization;
|
|
|
|
namespace Compat.Runtime.Serialization
|
|
{
|
|
using DataContractDictionary = Dictionary<XmlQualifiedName, DataContract>;
|
|
|
|
internal delegate IXmlSerializable CreateXmlSerializableDelegate();
|
|
|
|
internal sealed class XmlDataContract : DataContract
|
|
{
|
|
private readonly XmlDataContractCriticalHelper _helper;
|
|
|
|
internal XmlDataContract()
|
|
: base(new XmlDataContractCriticalHelper())
|
|
{
|
|
_helper = base.Helper as XmlDataContractCriticalHelper;
|
|
}
|
|
|
|
internal XmlDataContract(Type type)
|
|
: base(new XmlDataContractCriticalHelper(type))
|
|
{
|
|
_helper = base.Helper as XmlDataContractCriticalHelper;
|
|
}
|
|
|
|
internal override DataContractDictionary KnownDataContracts
|
|
{
|
|
get => _helper.KnownDataContracts;
|
|
set => _helper.KnownDataContracts = value;
|
|
}
|
|
|
|
internal XmlSchemaType XsdType
|
|
{
|
|
get => _helper.XsdType;
|
|
set => _helper.XsdType = value;
|
|
}
|
|
|
|
internal bool IsAnonymous => _helper.IsAnonymous;
|
|
|
|
internal override bool HasRoot
|
|
{
|
|
get => _helper.HasRoot;
|
|
set => _helper.HasRoot = value;
|
|
}
|
|
|
|
internal override XmlDictionaryString TopLevelElementName
|
|
{
|
|
get => _helper.TopLevelElementName;
|
|
set => _helper.TopLevelElementName = value;
|
|
}
|
|
|
|
internal override XmlDictionaryString TopLevelElementNamespace
|
|
{
|
|
get => _helper.TopLevelElementNamespace;
|
|
set => _helper.TopLevelElementNamespace = value;
|
|
}
|
|
|
|
internal bool IsTopLevelElementNullable
|
|
{
|
|
get => _helper.IsTopLevelElementNullable;
|
|
set => _helper.IsTopLevelElementNullable = value;
|
|
}
|
|
|
|
internal bool IsTypeDefinedOnImport
|
|
{
|
|
get => _helper.IsTypeDefinedOnImport;
|
|
set => _helper.IsTypeDefinedOnImport = value;
|
|
}
|
|
|
|
internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate
|
|
{
|
|
get
|
|
{
|
|
if (_helper.CreateXmlSerializableDelegate == null)
|
|
{
|
|
lock (this)
|
|
{
|
|
if (_helper.CreateXmlSerializableDelegate == null)
|
|
{
|
|
CreateXmlSerializableDelegate tempCreateXmlSerializable = GenerateCreateXmlSerializableDelegate();
|
|
Thread.MemoryBarrier();
|
|
_helper.CreateXmlSerializableDelegate = tempCreateXmlSerializable;
|
|
}
|
|
}
|
|
}
|
|
return _helper.CreateXmlSerializableDelegate;
|
|
}
|
|
}
|
|
|
|
internal override bool CanContainReferences => false;
|
|
|
|
internal override bool IsBuiltInDataContract => UnderlyingType == Globals.TypeOfXmlElement || UnderlyingType == Globals.TypeOfXmlNodeArray;
|
|
|
|
private class XmlDataContractCriticalHelper : DataContract.DataContractCriticalHelper
|
|
{
|
|
private DataContractDictionary knownDataContracts;
|
|
private bool isKnownTypeAttributeChecked;
|
|
private XmlDictionaryString topLevelElementName;
|
|
private XmlDictionaryString topLevelElementNamespace;
|
|
private bool isTopLevelElementNullable;
|
|
private bool isTypeDefinedOnImport;
|
|
private XmlSchemaType xsdType;
|
|
private bool hasRoot;
|
|
private CreateXmlSerializableDelegate createXmlSerializable;
|
|
|
|
internal XmlDataContractCriticalHelper()
|
|
{
|
|
}
|
|
|
|
internal XmlDataContractCriticalHelper(Type type)
|
|
: base(type)
|
|
{
|
|
if (type.IsDefined(Globals.TypeOfDataContractAttribute, false))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new System.Runtime.Serialization.InvalidDataContractException(SR.Format(SR.IXmlSerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type))));
|
|
}
|
|
|
|
if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new System.Runtime.Serialization.InvalidDataContractException(SR.Format(SR.IXmlSerializableCannotHaveCollectionDataContract, DataContract.GetClrTypeFullName(type))));
|
|
}
|
|
|
|
SchemaExporter.GetXmlTypeInfo(type, out XmlQualifiedName stableName, out XmlSchemaType xsdType, out bool hasRoot);
|
|
|
|
StableName = stableName;
|
|
XsdType = xsdType;
|
|
HasRoot = hasRoot;
|
|
XmlDictionary dictionary = new XmlDictionary();
|
|
Name = dictionary.Add(StableName.Name);
|
|
Namespace = dictionary.Add(StableName.Namespace);
|
|
object[] xmlRootAttributes = (UnderlyingType == null) ? null : UnderlyingType.GetCustomAttributes(Globals.TypeOfXmlRootAttribute, false);
|
|
if (xmlRootAttributes == null || xmlRootAttributes.Length == 0)
|
|
{
|
|
if (hasRoot)
|
|
{
|
|
topLevelElementName = Name;
|
|
topLevelElementNamespace = (StableName.Namespace == Globals.SchemaNamespace) ? DictionaryGlobals.EmptyString : Namespace;
|
|
isTopLevelElementNullable = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hasRoot)
|
|
{
|
|
XmlRootAttribute xmlRootAttribute = (XmlRootAttribute)xmlRootAttributes[0];
|
|
isTopLevelElementNullable = xmlRootAttribute.IsNullable;
|
|
string elementName = xmlRootAttribute.ElementName;
|
|
topLevelElementName = (elementName == null || elementName.Length == 0) ? Name : dictionary.Add(DataContract.EncodeLocalName(elementName));
|
|
string elementNs = xmlRootAttribute.Namespace;
|
|
topLevelElementNamespace = (elementNs == null || elementNs.Length == 0) ? DictionaryGlobals.EmptyString : dictionary.Add(elementNs);
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new System.Runtime.Serialization.InvalidDataContractException(SR.Format(SR.IsAnyCannotHaveXmlRoot, DataContract.GetClrTypeFullName(UnderlyingType))));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override DataContractDictionary KnownDataContracts
|
|
{
|
|
get
|
|
{
|
|
if (!isKnownTypeAttributeChecked && UnderlyingType != null)
|
|
{
|
|
lock (this)
|
|
{
|
|
if (!isKnownTypeAttributeChecked)
|
|
{
|
|
knownDataContracts = DataContract.ImportKnownTypeAttributes(UnderlyingType);
|
|
Thread.MemoryBarrier();
|
|
isKnownTypeAttributeChecked = true;
|
|
}
|
|
}
|
|
}
|
|
return knownDataContracts;
|
|
}
|
|
set => knownDataContracts = value;
|
|
}
|
|
|
|
internal XmlSchemaType XsdType
|
|
{
|
|
get => xsdType;
|
|
set => xsdType = value;
|
|
}
|
|
|
|
internal bool IsAnonymous => xsdType != null;
|
|
|
|
internal override bool HasRoot
|
|
{
|
|
get => hasRoot;
|
|
set => hasRoot = value;
|
|
}
|
|
|
|
internal override XmlDictionaryString TopLevelElementName
|
|
{
|
|
get => topLevelElementName;
|
|
set => topLevelElementName = value;
|
|
}
|
|
|
|
internal override XmlDictionaryString TopLevelElementNamespace
|
|
{
|
|
get => topLevelElementNamespace;
|
|
set => topLevelElementNamespace = value;
|
|
}
|
|
|
|
internal bool IsTopLevelElementNullable
|
|
{
|
|
get => isTopLevelElementNullable;
|
|
set => isTopLevelElementNullable = value;
|
|
}
|
|
|
|
internal bool IsTypeDefinedOnImport
|
|
{
|
|
get => isTypeDefinedOnImport;
|
|
set => isTypeDefinedOnImport = value;
|
|
}
|
|
|
|
internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate
|
|
{
|
|
get => createXmlSerializable;
|
|
set => createXmlSerializable = value;
|
|
}
|
|
|
|
}
|
|
|
|
private ConstructorInfo GetConstructor()
|
|
{
|
|
Type type = UnderlyingType;
|
|
|
|
if (type.IsValueType)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ConstructorInfo ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
|
|
if (ctor == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new System.Runtime.Serialization.InvalidDataContractException(SR.Format(SR.IXmlSerializableMustHaveDefaultConstructor, DataContract.GetClrTypeFullName(type))));
|
|
}
|
|
|
|
return ctor;
|
|
}
|
|
|
|
internal void SetTopLevelElementName(XmlQualifiedName elementName)
|
|
{
|
|
if (elementName != null)
|
|
{
|
|
XmlDictionary dictionary = new XmlDictionary();
|
|
TopLevelElementName = dictionary.Add(elementName.Name);
|
|
TopLevelElementNamespace = dictionary.Add(elementName.Namespace);
|
|
}
|
|
}
|
|
|
|
internal CreateXmlSerializableDelegate GenerateCreateXmlSerializableDelegate()
|
|
{
|
|
Type type = UnderlyingType;
|
|
CodeGenerator ilg = new CodeGenerator();
|
|
bool memberAccessFlag = RequiresMemberAccessForCreate(null);
|
|
ilg.BeginMethod("Create" + DataContract.GetClrTypeFullName(type), typeof(CreateXmlSerializableDelegate), memberAccessFlag);
|
|
if (type.IsValueType)
|
|
{
|
|
System.Reflection.Emit.LocalBuilder local = ilg.DeclareLocal(type, type.Name + "Value");
|
|
ilg.Ldloca(local);
|
|
ilg.InitObj(type);
|
|
ilg.Ldloc(local);
|
|
}
|
|
else
|
|
{
|
|
ilg.New(GetConstructor());
|
|
}
|
|
ilg.ConvertValue(UnderlyingType, Globals.TypeOfIXmlSerializable);
|
|
ilg.Ret();
|
|
return (CreateXmlSerializableDelegate)ilg.EndMethod();
|
|
}
|
|
|
|
private bool RequiresMemberAccessForCreate(SecurityException securityException)
|
|
{
|
|
if (!IsTypeVisible(UnderlyingType))
|
|
{
|
|
if (securityException != null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new SecurityException(SR.Format(SR.PartialTrustIXmlSerializableTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)),
|
|
securityException));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (ConstructorRequiresMemberAccess(GetConstructor()))
|
|
{
|
|
if (securityException != null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new SecurityException(SR.Format(SR.PartialTrustIXmlSerialzableNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)),
|
|
securityException));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal override bool Equals(object other, Dictionary<DataContractPairKey, object> checkedContracts)
|
|
{
|
|
if (IsEqualOrChecked(other, checkedContracts))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
XmlDataContract dataContract = other as XmlDataContract;
|
|
if (dataContract != null)
|
|
{
|
|
if (HasRoot != dataContract.HasRoot)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (IsAnonymous)
|
|
{
|
|
return dataContract.IsAnonymous;
|
|
}
|
|
else
|
|
{
|
|
return (StableName.Name == dataContract.StableName.Name && StableName.Namespace == dataContract.StableName.Namespace);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return base.GetHashCode();
|
|
}
|
|
|
|
public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
XmlObjectSerializerWriteContext.WriteRootIXmlSerializable(xmlWriter, obj);
|
|
}
|
|
else
|
|
{
|
|
context.WriteIXmlSerializable(xmlWriter, obj);
|
|
}
|
|
}
|
|
|
|
public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
|
|
{
|
|
object o;
|
|
if (context == null)
|
|
{
|
|
o = XmlObjectSerializerReadContext.ReadRootIXmlSerializable(xmlReader, this, true /*isMemberType*/);
|
|
}
|
|
else
|
|
{
|
|
o = context.ReadIXmlSerializable(xmlReader, this, true /*isMemberType*/);
|
|
context.AddNewObject(o);
|
|
}
|
|
xmlReader.ReadEndElement();
|
|
return o;
|
|
}
|
|
|
|
}
|
|
}
|