File: System\Runtime\Serialization\Json\ReflectionJsonFormatWriter.cs
Web Access
Project: src\src\libraries\System.Private.DataContractSerialization\src\System.Private.DataContractSerialization.csproj (System.Private.DataContractSerialization)
// 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.Linq;
using System.Reflection;
using System.Runtime.Serialization.DataContracts;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
 
namespace System.Runtime.Serialization.Json
{
    internal sealed class ReflectionJsonFormatWriter
    {
        private readonly ReflectionJsonClassWriter _reflectionClassWriter = new ReflectionJsonClassWriter();
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        public void ReflectionWriteClass(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract classContract, XmlDictionaryString[]? memberNames)
        {
            _reflectionClassWriter.ReflectionWriteClass(xmlWriter, obj, context, classContract, memberNames);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        public static void ReflectionWriteCollection(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract collectionContract)
        {
            if (xmlWriter is not JsonWriterDelegator jsonWriter)
            {
                throw new ArgumentException(nameof(xmlWriter));
            }
 
            XmlDictionaryString itemName = XmlObjectSerializerWriteContextComplexJson.CollectionItemName;
 
            if (collectionContract.Kind == CollectionKind.Array)
            {
                context.IncrementArrayCount(jsonWriter, (Array)obj);
                Type itemType = collectionContract.ItemType;
                if (!ReflectionTryWritePrimitiveArray(jsonWriter, obj, itemType, itemName))
                {
                    ReflectionWriteArrayAttribute(jsonWriter);
 
                    Array array = (Array)obj;
                    PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
                    for (int i = 0; i < array.Length; ++i)
                    {
                        ReflectionJsonClassWriter.ReflectionWriteStartElement(jsonWriter, itemName);
                        ReflectionJsonClassWriter.ReflectionWriteValue(jsonWriter, context, itemType, array.GetValue(i), false, primitiveContract);
                        ReflectionJsonClassWriter.ReflectionWriteEndElement(jsonWriter);
                    }
                }
            }
            else
            {
                collectionContract.IncrementCollectionCount(jsonWriter, obj, context);
 
                IEnumerator enumerator = collectionContract.GetEnumeratorForCollection(obj);
 
                bool canWriteSimpleDictionary = collectionContract.Kind == CollectionKind.GenericDictionary
                                             || collectionContract.Kind == CollectionKind.Dictionary;
 
                bool useSimpleDictionaryFormat = context.UseSimpleDictionaryFormat;
 
                if (canWriteSimpleDictionary && useSimpleDictionaryFormat)
                {
                    ReflectionWriteObjectAttribute(jsonWriter);
                    Type[] itemTypeGenericArguments = collectionContract.ItemType.GetGenericArguments();
                    Type? dictionaryValueType = itemTypeGenericArguments.Length == 2 ? itemTypeGenericArguments[1] : null;
 
                    while (enumerator.MoveNext())
                    {
                        object current = enumerator.Current;
                        object key = ((IKeyValue)current).Key!;
                        object value = ((IKeyValue)current).Value!;
                        ReflectionJsonClassWriter.ReflectionWriteStartElement(jsonWriter, key.ToString()!);
                        ReflectionJsonClassWriter.ReflectionWriteValue(jsonWriter, context, dictionaryValueType ?? value.GetType(), value, false, primitiveContractForParamType: null);
                        ReflectionJsonClassWriter.ReflectionWriteEndElement(jsonWriter);
                    }
                }
                else
                {
                    ReflectionWriteArrayAttribute(jsonWriter);
 
                    PrimitiveDataContract? primitiveContractForType = PrimitiveDataContract.GetPrimitiveDataContract(collectionContract.UnderlyingType);
                    if (primitiveContractForType != null && primitiveContractForType.UnderlyingType != Globals.TypeOfObject)
                    {
                        while (enumerator.MoveNext())
                        {
                            object current = enumerator.Current;
                            context.IncrementItemCount(1);
                            primitiveContractForType.WriteXmlElement(jsonWriter, current, context, itemName, null /*namespace*/);
                        }
                    }
                    else
                    {
                        Type elementType = collectionContract.GetCollectionElementType();
                        bool isDictionary = collectionContract.Kind == CollectionKind.Dictionary || collectionContract.Kind == CollectionKind.GenericDictionary;
 
                        DataContract? itemContract;
                        JsonDataContract? jsonDataContract = null;
                        if (isDictionary)
                        {
                            itemContract = XmlObjectSerializerWriteContextComplexJson.GetRevisedItemContract(collectionContract.ItemContract);
                            jsonDataContract = JsonDataContract.GetJsonDataContract(itemContract);
                        }
 
                        while (enumerator.MoveNext())
                        {
                            object current = enumerator.Current;
                            context.IncrementItemCount(1);
                            ReflectionJsonClassWriter.ReflectionWriteStartElement(jsonWriter, itemName);
                            if (isDictionary)
                            {
                                jsonDataContract!.WriteJsonValue(jsonWriter, current, context, collectionContract.ItemType.TypeHandle);
                            }
                            else
                            {
                                ReflectionJsonClassWriter.ReflectionWriteValue(jsonWriter, context, elementType, current, false, primitiveContractForParamType: null);
                            }
 
                            ReflectionJsonClassWriter.ReflectionWriteEndElement(jsonWriter);
                        }
                    }
                }
            }
        }
 
        private static void ReflectionWriteObjectAttribute(XmlWriterDelegator xmlWriter)
        {
            xmlWriter.WriteAttributeString(
                prefix: null,
                localName: JsonGlobals.typeString,
                ns: null,
                value: JsonGlobals.objectString);
        }
 
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        private static bool ReflectionTryWritePrimitiveArray(JsonWriterDelegator jsonWriter, object obj, Type itemType, XmlDictionaryString collectionItemName)
        {
            PrimitiveDataContract? primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(itemType);
            if (primitiveContract == null)
                return false;
 
            XmlDictionaryString? itemNamespace = null;
 
            switch (Type.GetTypeCode(itemType))
            {
                case TypeCode.Boolean:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonBooleanArray((bool[])obj, collectionItemName, itemNamespace);
                    break;
                case TypeCode.DateTime:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonDateTimeArray((DateTime[])obj, collectionItemName, itemNamespace);
                    break;
                case TypeCode.Decimal:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonDecimalArray((decimal[])obj, collectionItemName, itemNamespace);
                    break;
                case TypeCode.Int32:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonInt32Array((int[])obj, collectionItemName, itemNamespace);
                    break;
                case TypeCode.Int64:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonInt64Array((long[])obj, collectionItemName, itemNamespace);
                    break;
                case TypeCode.Single:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonSingleArray((float[])obj, collectionItemName, itemNamespace);
                    break;
                case TypeCode.Double:
                    ReflectionWriteArrayAttribute(jsonWriter);
                    jsonWriter.WriteJsonDoubleArray((double[])obj, collectionItemName, itemNamespace);
                    break;
                default:
                    return false;
            }
            return true;
        }
 
        private static void ReflectionWriteArrayAttribute(XmlWriterDelegator xmlWriter)
        {
            xmlWriter.WriteAttributeString(
                prefix: null,
                localName: JsonGlobals.typeString,
                ns: string.Empty,
                value: JsonGlobals.arrayString);
        }
    }
 
    internal sealed class ReflectionJsonClassWriter : ReflectionClassWriter
    {
        [RequiresDynamicCode(DataContract.SerializerAOTWarning)]
        [RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
        protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? memberNames)
        {
            Debug.Assert(memberNames != null);
 
            int memberCount = (classContract.BaseClassContract == null) ? 0 :
                ReflectionWriteMembers(xmlWriter, obj, context, classContract.BaseClassContract, derivedMostClassContract, childElementIndex, memberNames);
 
            childElementIndex += memberCount;
 
            context.IncrementItemCount(classContract.Members!.Count);
            for (int i = 0; i < classContract.Members.Count; i++, memberCount++)
            {
                DataMember member = classContract.Members[i];
                Type memberType = member.MemberType;
                if (member.IsGetOnlyCollection)
                {
                    context.StoreIsGetOnlyCollection();
                }
                else
                {
                    context.ResetIsGetOnlyCollection();
                }
 
 
                bool shouldWriteValue = true;
                object? memberValue = null;
                if (!member.EmitDefaultValue)
                {
                    memberValue = ReflectionGetMemberValue(obj, member);
                    object? defaultValue = XmlFormatGeneratorStatics.GetDefaultValue(memberType);
                    if ((memberValue == null && defaultValue == null)
                        || (memberValue != null && memberValue.Equals(defaultValue)))
                    {
                        shouldWriteValue = false;
 
                        if (member.IsRequired)
                        {
                            XmlObjectSerializerWriteContext.ThrowRequiredMemberMustBeEmitted(member.Name, classContract.UnderlyingType);
                        }
                    }
                }
 
                if (shouldWriteValue)
                {
                    memberValue ??= ReflectionGetMemberValue(obj, member);
                    bool requiresNameAttribute = DataContractJsonSerializer.CheckIfXmlNameRequiresMapping(classContract.MemberNames![i]);
                    PrimitiveDataContract? primitiveContract = member.MemberPrimitiveContract;
                    if (requiresNameAttribute || !ReflectionTryWritePrimitive(xmlWriter, context, memberValue, memberNames[i + childElementIndex] /*name*/, null/*ns*/, primitiveContract))
                    {
                        // Note: DataContractSerializer has member-conflict logic here to deal with the schema export
                        //       requirement that the same member can't be of two different types.
                        if (requiresNameAttribute)
                        {
                            XmlObjectSerializerWriteContextComplexJson.WriteJsonNameWithMapping(xmlWriter, memberNames, i + childElementIndex);
                        }
                        else
                        {
                            ReflectionWriteStartElement(xmlWriter, memberNames[i + childElementIndex]);
                        }
 
                        ReflectionWriteValue(xmlWriter, context, memberType, memberValue, false/*writeXsiType*/, primitiveContractForParamType: null);
                        ReflectionWriteEndElement(xmlWriter);
                    }
 
                    if (classContract.HasExtensionData)
                    {
                        context.WriteExtensionData(xmlWriter, ((IExtensibleDataObject)obj).ExtensionData, memberCount);
                    }
                }
            }
 
            return memberCount;
        }
 
        public static void ReflectionWriteStartElement(XmlWriterDelegator xmlWriter, XmlDictionaryString name)
        {
            xmlWriter.WriteStartElement(name, null);
        }
 
        public static void ReflectionWriteStartElement(XmlWriterDelegator xmlWriter, string name)
        {
            xmlWriter.WriteStartElement(name, null);
        }
 
        public static void ReflectionWriteEndElement(XmlWriterDelegator xmlWriter)
        {
            xmlWriter.WriteEndElement();
        }
    }
}