94b2861243
Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
421 lines
19 KiB
C#
421 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Reflection;
|
|
#if !MONO
|
|
using System.ServiceModel;
|
|
#endif
|
|
using System.Collections;
|
|
|
|
namespace System.Runtime.Serialization.Json
|
|
{
|
|
#if USE_REFEMIT
|
|
public class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
|
|
#else
|
|
internal class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex
|
|
#endif
|
|
{
|
|
EmitTypeInformation emitXsiType;
|
|
bool perCallXsiTypeAlreadyEmitted;
|
|
bool useSimpleDictionaryFormat;
|
|
|
|
public XmlObjectSerializerWriteContextComplexJson(DataContractJsonSerializer serializer, DataContract rootTypeDataContract)
|
|
: base(serializer,
|
|
serializer.MaxItemsInObjectGraph,
|
|
new StreamingContext(StreamingContextStates.All),
|
|
serializer.IgnoreExtensionDataObject)
|
|
{
|
|
this.emitXsiType = serializer.EmitTypeInformation;
|
|
this.rootTypeDataContract = rootTypeDataContract;
|
|
this.serializerKnownTypeList = serializer.knownTypeList;
|
|
this.dataContractSurrogate = serializer.DataContractSurrogate;
|
|
this.serializeReadOnlyTypes = serializer.SerializeReadOnlyTypes;
|
|
this.useSimpleDictionaryFormat = serializer.UseSimpleDictionaryFormat;
|
|
}
|
|
|
|
internal static XmlObjectSerializerWriteContextComplexJson CreateContext(DataContractJsonSerializer serializer, DataContract rootTypeDataContract)
|
|
{
|
|
return new XmlObjectSerializerWriteContextComplexJson(serializer, rootTypeDataContract);
|
|
}
|
|
|
|
internal IList<Type> SerializerKnownTypeList
|
|
{
|
|
get
|
|
{
|
|
return this.serializerKnownTypeList;
|
|
}
|
|
}
|
|
|
|
public bool UseSimpleDictionaryFormat
|
|
{
|
|
get
|
|
{
|
|
return this.useSimpleDictionaryFormat;
|
|
}
|
|
}
|
|
|
|
internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, Type dataContractType, string clrTypeName, string clrAssemblyName)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
internal override bool WriteClrTypeInfo(XmlWriterDelegator xmlWriter, DataContract dataContract)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
internal override void WriteArraySize(XmlWriterDelegator xmlWriter, int size)
|
|
{
|
|
}
|
|
|
|
protected override void WriteTypeInfo(XmlWriterDelegator writer, string dataContractName, string dataContractNamespace)
|
|
{
|
|
if (this.emitXsiType != EmitTypeInformation.Never)
|
|
{
|
|
if (string.IsNullOrEmpty(dataContractNamespace))
|
|
{
|
|
WriteTypeInfo(writer, dataContractName);
|
|
}
|
|
else
|
|
{
|
|
WriteTypeInfo(writer, string.Concat(dataContractName, JsonGlobals.NameValueSeparatorString, TruncateDefaultDataContractNamespace(dataContractNamespace)));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static string TruncateDefaultDataContractNamespace(string dataContractNamespace)
|
|
{
|
|
if (!string.IsNullOrEmpty(dataContractNamespace))
|
|
{
|
|
if (dataContractNamespace[0] == '#')
|
|
{
|
|
return string.Concat("\\", dataContractNamespace);
|
|
}
|
|
else if (dataContractNamespace[0] == '\\')
|
|
{
|
|
return string.Concat("\\", dataContractNamespace);
|
|
}
|
|
else if (dataContractNamespace.StartsWith(Globals.DataContractXsdBaseNamespace, StringComparison.Ordinal))
|
|
{
|
|
return string.Concat("#", dataContractNamespace.Substring(JsonGlobals.DataContractXsdBaseNamespaceLength));
|
|
}
|
|
}
|
|
|
|
return dataContractNamespace;
|
|
}
|
|
|
|
static bool RequiresJsonTypeInfo(DataContract contract)
|
|
{
|
|
return (contract is ClassDataContract);
|
|
}
|
|
|
|
void WriteTypeInfo(XmlWriterDelegator writer, string typeInformation)
|
|
{
|
|
writer.WriteAttributeString(null, JsonGlobals.serverTypeString, null, typeInformation);
|
|
}
|
|
|
|
protected override bool WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
|
|
{
|
|
if (!((object.ReferenceEquals(contract.Name, declaredContract.Name) &&
|
|
object.ReferenceEquals(contract.Namespace, declaredContract.Namespace)) ||
|
|
(contract.Name.Value == declaredContract.Name.Value &&
|
|
contract.Namespace.Value == declaredContract.Namespace.Value)) &&
|
|
(contract.UnderlyingType != Globals.TypeOfObjectArray) &&
|
|
(this.emitXsiType != EmitTypeInformation.Never))
|
|
{
|
|
// We always deserialize collections assigned to System.Object as object[]
|
|
// Because of its common and JSON-specific nature,
|
|
// we don't want to validate known type information for object[]
|
|
|
|
// Don't validate known type information when emitXsiType == Never because
|
|
// known types are not used without type information in the JSON
|
|
|
|
if (RequiresJsonTypeInfo(contract))
|
|
{
|
|
perCallXsiTypeAlreadyEmitted = true;
|
|
WriteTypeInfo(writer, contract.Name.Value, contract.Namespace.Value);
|
|
}
|
|
else
|
|
{
|
|
// check if the declared type is System.Enum and throw because
|
|
// __type information cannot be written for enums since it results in invalid JSON.
|
|
// Without __type, the resulting JSON cannot be deserialized since a number cannot be directly assigned to System.Enum.
|
|
if (declaredContract.UnderlyingType == typeof(Enum))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException
|
|
(SR.GetString(SR.EnumTypeNotSupportedByDataContractJsonSerializer, declaredContract.UnderlyingType)));
|
|
}
|
|
|
|
}
|
|
// Return true regardless of whether we actually wrote __type information
|
|
// E.g. We don't write __type information for enums, but we still want verifyKnownType
|
|
// to be true for them.
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
#if USE_REFEMIT
|
|
public void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
|
|
#else
|
|
internal void WriteJsonISerializable(XmlWriterDelegator xmlWriter, ISerializable obj)
|
|
#endif
|
|
{
|
|
Type objType = obj.GetType();
|
|
SerializationInfo serInfo = new SerializationInfo(objType, XmlObjectSerializer.FormatterConverter);
|
|
GetObjectData(obj, serInfo, GetStreamingContext());
|
|
if (DataContract.GetClrTypeFullName(objType) != serInfo.FullTypeName)
|
|
{
|
|
throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ChangingFullTypeNameNotSupported, serInfo.FullTypeName, DataContract.GetClrTypeFullName(objType))));
|
|
}
|
|
else
|
|
{
|
|
base.WriteSerializationInfo(xmlWriter, objType, serInfo);
|
|
}
|
|
}
|
|
|
|
|
|
#if USE_REFEMIT
|
|
public static DataContract GetRevisedItemContract(DataContract oldItemContract)
|
|
#else
|
|
internal static DataContract GetRevisedItemContract(DataContract oldItemContract)
|
|
#endif
|
|
{
|
|
if ((oldItemContract != null) &&
|
|
oldItemContract.UnderlyingType.IsGenericType &&
|
|
(oldItemContract.UnderlyingType.GetGenericTypeDefinition() == Globals.TypeOfKeyValue))
|
|
{
|
|
return DataContract.GetDataContract(oldItemContract.UnderlyingType);
|
|
}
|
|
return oldItemContract;
|
|
}
|
|
|
|
protected override void WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle declaredTypeHandle)
|
|
{
|
|
JsonDataContract jsonDataContract = JsonDataContract.GetJsonDataContract(dataContract);
|
|
if (emitXsiType == EmitTypeInformation.Always && !perCallXsiTypeAlreadyEmitted && RequiresJsonTypeInfo(dataContract))
|
|
{
|
|
WriteTypeInfo(xmlWriter, jsonDataContract.TypeName);
|
|
}
|
|
perCallXsiTypeAlreadyEmitted = false;
|
|
DataContractJsonSerializer.WriteJsonValue(jsonDataContract, xmlWriter, obj, this, declaredTypeHandle);
|
|
}
|
|
|
|
protected override void WriteNull(XmlWriterDelegator xmlWriter)
|
|
{
|
|
DataContractJsonSerializer.WriteJsonNull(xmlWriter);
|
|
}
|
|
|
|
#if USE_REFEMIT
|
|
public XmlDictionaryString CollectionItemName
|
|
#else
|
|
internal XmlDictionaryString CollectionItemName
|
|
#endif
|
|
{
|
|
get { return JsonGlobals.itemDictionaryString; }
|
|
}
|
|
|
|
#if USE_REFEMIT
|
|
public static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlDictionaryString[] memberNames, int index)
|
|
#else
|
|
internal static void WriteJsonNameWithMapping(XmlWriterDelegator xmlWriter, XmlDictionaryString[] memberNames, int index)
|
|
#endif
|
|
{
|
|
xmlWriter.WriteStartElement("a", JsonGlobals.itemString, JsonGlobals.itemString);
|
|
xmlWriter.WriteAttributeString(null, JsonGlobals.itemString, null, memberNames[index].Value);
|
|
}
|
|
|
|
internal override void WriteExtensionDataTypeInfo(XmlWriterDelegator xmlWriter, IDataNode dataNode)
|
|
{
|
|
Type dataType = dataNode.DataType;
|
|
if (dataType == Globals.TypeOfClassDataNode ||
|
|
dataType == Globals.TypeOfISerializableDataNode)
|
|
{
|
|
xmlWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.objectString);
|
|
base.WriteExtensionDataTypeInfo(xmlWriter, dataNode);
|
|
}
|
|
else if (dataType == Globals.TypeOfCollectionDataNode)
|
|
{
|
|
xmlWriter.WriteAttributeString(null, JsonGlobals.typeString, null, JsonGlobals.arrayString);
|
|
// Don't write __type for collections
|
|
}
|
|
else if (dataType == Globals.TypeOfXmlDataNode)
|
|
{
|
|
// Don't write type or __type for XML types because we serialize them to strings
|
|
}
|
|
else if ((dataType == Globals.TypeOfObject) && (dataNode.Value != null))
|
|
{
|
|
DataContract dc = GetDataContract(dataNode.Value.GetType());
|
|
if (RequiresJsonTypeInfo(dc))
|
|
{
|
|
base.WriteExtensionDataTypeInfo(xmlWriter, dataNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void SerializeWithXsiType(XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
|
|
{
|
|
DataContract dataContract;
|
|
bool verifyKnownType = false;
|
|
bool isDeclaredTypeInterface = declaredType.IsInterface;
|
|
|
|
if (isDeclaredTypeInterface && CollectionDataContract.IsCollectionInterface(declaredType))
|
|
{
|
|
dataContract = GetDataContract(declaredTypeHandle, declaredType);
|
|
}
|
|
else if (declaredType.IsArray) // If declared type is array do not write __serverType. Instead write__serverType for each item
|
|
{
|
|
dataContract = GetDataContract(declaredTypeHandle, declaredType);
|
|
}
|
|
else
|
|
{
|
|
dataContract = GetDataContract(objectTypeHandle, objectType);
|
|
DataContract declaredTypeContract = (declaredTypeID >= 0)
|
|
? GetDataContract(declaredTypeID, declaredTypeHandle)
|
|
: GetDataContract(declaredTypeHandle, declaredType);
|
|
verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, declaredTypeContract);
|
|
HandleCollectionAssignedToObject(declaredType, ref dataContract, ref obj, ref verifyKnownType);
|
|
}
|
|
|
|
if (isDeclaredTypeInterface)
|
|
{
|
|
VerifyObjectCompatibilityWithInterface(dataContract, obj, declaredType);
|
|
}
|
|
SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredType.TypeHandle, declaredType);
|
|
}
|
|
|
|
static void VerifyObjectCompatibilityWithInterface(DataContract contract, object graph, Type declaredType)
|
|
{
|
|
Type contractType = contract.GetType();
|
|
if ((contractType == typeof(XmlDataContract)) && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(declaredType))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.XmlObjectAssignedToIncompatibleInterface, graph.GetType(), declaredType)));
|
|
}
|
|
|
|
if ((contractType == typeof(CollectionDataContract)) && !CollectionDataContract.IsCollectionInterface(declaredType))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.CollectionAssignedToIncompatibleInterface, graph.GetType(), declaredType)));
|
|
}
|
|
}
|
|
|
|
void HandleCollectionAssignedToObject(Type declaredType, ref DataContract dataContract, ref object obj, ref bool verifyKnownType)
|
|
{
|
|
if ((declaredType != dataContract.UnderlyingType) && (dataContract is CollectionDataContract))
|
|
{
|
|
if (verifyKnownType)
|
|
{
|
|
VerifyType(dataContract, declaredType);
|
|
verifyKnownType = false;
|
|
}
|
|
if (((CollectionDataContract)dataContract).Kind == CollectionKind.Dictionary)
|
|
{
|
|
// Convert non-generic dictionary to generic dictionary
|
|
IDictionary dictionaryObj = obj as IDictionary;
|
|
Dictionary<object, object> genericDictionaryObj = new Dictionary<object, object>();
|
|
foreach (DictionaryEntry entry in dictionaryObj)
|
|
{
|
|
genericDictionaryObj.Add(entry.Key, entry.Value);
|
|
}
|
|
obj = genericDictionaryObj;
|
|
}
|
|
dataContract = GetDataContract(Globals.TypeOfIEnumerable);
|
|
}
|
|
}
|
|
|
|
internal override void SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType)
|
|
{
|
|
bool verifyKnownType = false;
|
|
Type declaredType = rootTypeDataContract.UnderlyingType;
|
|
bool isDeclaredTypeInterface = declaredType.IsInterface;
|
|
|
|
if (!(isDeclaredTypeInterface && CollectionDataContract.IsCollectionInterface(declaredType))
|
|
&& !declaredType.IsArray)//Array covariance is not supported in XSD. If declared type is array do not write xsi:type. Instead write xsi:type for each item
|
|
{
|
|
verifyKnownType = WriteTypeInfo(xmlWriter, dataContract, rootTypeDataContract);
|
|
HandleCollectionAssignedToObject(declaredType, ref dataContract, ref obj, ref verifyKnownType);
|
|
}
|
|
|
|
if (isDeclaredTypeInterface)
|
|
{
|
|
VerifyObjectCompatibilityWithInterface(dataContract, obj, declaredType);
|
|
}
|
|
SerializeAndVerifyType(dataContract, xmlWriter, obj, verifyKnownType, declaredType.TypeHandle, declaredType);
|
|
}
|
|
|
|
void VerifyType(DataContract dataContract, Type declaredType)
|
|
{
|
|
bool knownTypesAddedInCurrentScope = false;
|
|
if (dataContract.KnownDataContracts != null)
|
|
{
|
|
scopedKnownTypes.Push(dataContract.KnownDataContracts);
|
|
knownTypesAddedInCurrentScope = true;
|
|
}
|
|
|
|
if (!IsKnownType(dataContract, declaredType))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.DcTypeNotFoundOnSerialize, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), dataContract.StableName.Name, dataContract.StableName.Namespace)));
|
|
}
|
|
|
|
if (knownTypesAddedInCurrentScope)
|
|
{
|
|
scopedKnownTypes.Pop();
|
|
}
|
|
}
|
|
|
|
internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type)
|
|
{
|
|
DataContract dataContract = base.GetDataContract(typeHandle, type);
|
|
DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
|
|
return dataContract;
|
|
}
|
|
|
|
internal override DataContract GetDataContractSkipValidation(int typeId, RuntimeTypeHandle typeHandle, Type type)
|
|
{
|
|
DataContract dataContract = base.GetDataContractSkipValidation(typeId, typeHandle, type);
|
|
DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
|
|
return dataContract;
|
|
}
|
|
|
|
internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle)
|
|
{
|
|
DataContract dataContract = base.GetDataContract(id, typeHandle);
|
|
DataContractJsonSerializer.CheckIfTypeIsReference(dataContract);
|
|
return dataContract;
|
|
}
|
|
|
|
internal static DataContract ResolveJsonDataContractFromRootDataContract(XmlObjectSerializerContext context, XmlQualifiedName typeQName, DataContract rootTypeDataContract)
|
|
{
|
|
if (rootTypeDataContract.StableName == typeQName)
|
|
return rootTypeDataContract;
|
|
|
|
CollectionDataContract collectionContract = rootTypeDataContract as CollectionDataContract;
|
|
while (collectionContract != null)
|
|
{
|
|
DataContract itemContract;
|
|
if (collectionContract.ItemType.IsGenericType
|
|
&& collectionContract.ItemType.GetGenericTypeDefinition() == typeof(KeyValue<,>))
|
|
{
|
|
itemContract = context.GetDataContract(Globals.TypeOfKeyValuePair.MakeGenericType(collectionContract.ItemType.GetGenericArguments()));
|
|
}
|
|
else
|
|
{
|
|
itemContract = context.GetDataContract(context.GetSurrogatedType(collectionContract.ItemType));
|
|
}
|
|
if (itemContract.StableName == typeQName)
|
|
{
|
|
return itemContract;
|
|
}
|
|
collectionContract = itemContract as CollectionDataContract;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected override DataContract ResolveDataContractFromRootDataContract(XmlQualifiedName typeQName)
|
|
{
|
|
return XmlObjectSerializerWriteContextComplexJson.ResolveJsonDataContractFromRootDataContract(this, typeQName, rootTypeDataContract);
|
|
}
|
|
}
|
|
}
|