File: System\Runtime\Serialization\Json\XmlObjectSerializerWriteContextComplexJson.cs
Project: ndp\cdf\src\WCF\Serialization\System.Runtime.Serialization.csproj (System.Runtime.Serialization)
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Reflection;
using System.ServiceModel;
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);
        }
    }
}