private void SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
        {
            object underlyingList = values is IWrappedCollection wrappedCollection ? wrappedCollection.UnderlyingCollection : values;

            OnSerializing(writer, contract, underlyingList);

            _serializeStack.Add(underlyingList);

            bool hasWrittenMetadataObject = WriteStartArray(writer, underlyingList, contract, member, collectionContract, containerProperty);

            writer.WriteStartArray();

            int initialDepth = writer.Top;

            int index = 0;

            // note that an error in the IEnumerable won't be caught
            foreach (object value in values)
            {
                try
                {
                    JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);

                    if (ShouldWriteReference(value, null, valueContract, contract, member))
                    {
                        WriteReference(writer, value);
                    }
                    else
                    {
                        if (CheckForCircularReference(writer, value, null, valueContract, contract, member))
                        {
                            SerializeValue(writer, value, valueContract, null, contract, member);
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (IsErrorHandled(underlyingList, contract, index, null, writer.ContainerPath, ex))
                    {
                        HandleError(writer, initialDepth);
                    }
                    else
                    {
                        throw;
                    }
                }
                finally
                {
                    index++;
                }
            }

            writer.WriteEndArray();

            if (hasWrittenMetadataObject)
            {
                writer.WriteEndObject();
            }

            _serializeStack.RemoveAt(_serializeStack.Count - 1);

            OnSerialized(writer, contract, underlyingList);
        }
        private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
        {
            OnSerializing(writer, contract, value);
            _serializeStack.Add(value);

            WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);

            int initialDepth = writer.Top;

            for (int index = 0; index < contract.Properties.Count; index++)
            {
                JsonProperty property = contract.Properties[index];

                // only write non-dynamic properties that have an explicit attribute
                if (property.HasMemberAttribute)
                {
                    try
                    {
                        if (!CalculatePropertyValues(writer, value, contract, member, property, out JsonContract memberContract, out object memberValue))
                        {
                            continue;
                        }

                        property.WritePropertyName(writer);
                        SerializeValue(writer, memberValue, memberContract, property, contract, member);
                    }
                    catch (Exception ex)
                    {
                        if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex))
                        {
                            HandleError(writer, initialDepth);
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }

            foreach (string memberName in value.GetDynamicMemberNames())
            {
                if (contract.TryGetMember(value, memberName, out object memberValue))
                {
                    try
                    {
                        JsonContract valueContract = GetContractSafe(memberValue);

                        if (!ShouldWriteDynamicProperty(memberValue))
                        {
                            continue;
                        }

                        if (CheckForCircularReference(writer, memberValue, null, valueContract, contract, member))
                        {
                            string resolvedPropertyName = (contract.PropertyNameResolver != null)
                                ? contract.PropertyNameResolver(memberName)
                                : memberName;

                            writer.WritePropertyName(resolvedPropertyName);
                            SerializeValue(writer, memberValue, valueContract, null, contract, member);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (IsErrorHandled(value, contract, memberName, null, writer.ContainerPath, ex))
                        {
                            HandleError(writer, initialDepth);
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }

            writer.WriteEndObject();

            _serializeStack.RemoveAt(_serializeStack.Count - 1);
            OnSerialized(writer, contract, value);
        }
        private bool CalculatePropertyValues(JsonWriter writer, object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, out JsonContract memberContract, out object memberValue)
        {
            if (!property.Ignored && property.Readable && ShouldSerialize(writer, property, value) && IsSpecified(writer, property, value))
            {
                if (property.PropertyContract == null)
                {
                    property.PropertyContract = Serializer._contractResolver.ResolveContract(property.PropertyType);
                }

                memberValue    = property.ValueProvider.GetValue(value);
                memberContract = property.PropertyContract.IsSealed ? property.PropertyContract : GetContractSafe(memberValue);

                if (ShouldWriteProperty(memberValue, contract as JsonObjectContract, property))
                {
                    if (ShouldWriteReference(memberValue, property, memberContract, contract, member))
                    {
                        property.WritePropertyName(writer);
                        WriteReference(writer, memberValue);
                        return(false);
                    }

                    if (!CheckForCircularReference(writer, memberValue, property, memberContract, contract, member))
                    {
                        return(false);
                    }

                    if (memberValue == null)
                    {
                        JsonObjectContract objectContract   = contract as JsonObjectContract;
                        Required           resolvedRequired = property._required ?? objectContract?.ItemRequired ?? Required.Default;
                        if (resolvedRequired == Required.Always)
                        {
                            throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
                        }
                        if (resolvedRequired == Required.DisallowNull)
                        {
                            throw JsonSerializationException.Create(null, writer.ContainerPath, "Cannot write a null value for property '{0}'. Property requires a non-null value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName), null);
                        }
                    }

                    return(true);
                }
            }

            memberContract = null;
            memberValue    = null;
            return(false);
        }
        private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract, JsonContainerContract collectionContract, JsonProperty containerProperty)
        {
            if (ShouldWriteReference(value, null, contract, collectionContract, containerProperty))
            {
                WriteReference(writer, value);
            }
            else
            {
                if (!CheckForCircularReference(writer, value, null, contract, collectionContract, containerProperty))
                {
                    return;
                }

                _serializeStack.Add(value);

                if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
                {
                    TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null);
                }

                converter.WriteJson(writer, value, GetInternalSerializer());

                if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
                {
                    TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null);
                }

                _serializeStack.RemoveAt(_serializeStack.Count - 1);
            }
        }
        private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
        {
            OnSerializing(writer, contract, value);

            _serializeStack.Add(value);

            WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty);

            int initialDepth = writer.Top;

            for (int index = 0; index < contract.Properties.Count; index++)
            {
                JsonProperty property = contract.Properties[index];
                try
                {
                    if (!CalculatePropertyValues(writer, value, contract, member, property, out JsonContract memberContract, out object memberValue))
                    {
                        continue;
                    }

                    property.WritePropertyName(writer);
                    SerializeValue(writer, memberValue, memberContract, property, contract, member);
                }
                catch (Exception ex)
                {
                    if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex))
                    {
                        HandleError(writer, initialDepth);
                    }
                    else
                    {
                        throw;
                    }
                }
            }

            IEnumerable <KeyValuePair <object, object> > extensionData = contract.ExtensionDataGetter?.Invoke(value);

            if (extensionData != null)
            {
                foreach (KeyValuePair <object, object> e in extensionData)
                {
                    JsonContract keyContract   = GetContractSafe(e.Key);
                    JsonContract valueContract = GetContractSafe(e.Value);

                    string propertyName = GetPropertyName(writer, e.Key, keyContract, out _);

                    propertyName = (contract.ExtensionDataNameResolver != null)
                        ? contract.ExtensionDataNameResolver(propertyName)
                        : propertyName;

                    if (ShouldWriteReference(e.Value, null, valueContract, contract, member))
                    {
                        writer.WritePropertyName(propertyName);
                        WriteReference(writer, e.Value);
                    }
                    else
                    {
                        if (!CheckForCircularReference(writer, e.Value, null, valueContract, contract, member))
                        {
                            continue;
                        }

                        writer.WritePropertyName(propertyName);

                        SerializeValue(writer, e.Value, valueContract, null, contract, member);
                    }
                }
            }

            writer.WriteEndObject();

            _serializeStack.RemoveAt(_serializeStack.Count - 1);

            OnSerialized(writer, contract, value);
        }
        private bool CheckForCircularReference(JsonWriter writer, object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
        {
            if (value == null || contract.ContractType == JsonContractType.Primitive || contract.ContractType == JsonContractType.String)
            {
                return(true);
            }

            ReferenceLoopHandling?referenceLoopHandling = null;

            if (property != null)
            {
                referenceLoopHandling = property.ReferenceLoopHandling;
            }

            if (referenceLoopHandling == null && containerProperty != null)
            {
                referenceLoopHandling = containerProperty.ItemReferenceLoopHandling;
            }

            if (referenceLoopHandling == null && containerContract != null)
            {
                referenceLoopHandling = containerContract.ItemReferenceLoopHandling;
            }

            bool exists = (Serializer._equalityComparer != null)
                ? _serializeStack.Contains(value, Serializer._equalityComparer)
                : _serializeStack.Contains(value);

            if (exists)
            {
                string message = "Self referencing loop detected";
                if (property != null)
                {
                    message += " for property '{0}'".FormatWith(CultureInfo.InvariantCulture, property.PropertyName);
                }
                message += " with type '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType());

                switch (referenceLoopHandling.GetValueOrDefault(Serializer._referenceLoopHandling))
                {
                case ReferenceLoopHandling.Error:
                    throw JsonSerializationException.Create(null, writer.ContainerPath, message, null);

                case ReferenceLoopHandling.Ignore:
                    if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
                    {
                        TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Skipping serializing self referenced value."), null);
                    }

                    return(false);

                case ReferenceLoopHandling.Serialize:
                    if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose)
                    {
                        TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Serializing self referenced value."), null);
                    }

                    return(true);
                }
            }

            return(true);
        }
        private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
        {
            if (value == null)
            {
                writer.WriteNull();
                return;
            }

            JsonConverter converter =
                member?.Converter ??
                containerProperty?.ItemConverter ??
                containerContract?.ItemConverter ??
                valueContract.Converter ??
                Serializer.GetMatchingConverter(valueContract.UnderlyingType) ??
                valueContract.InternalConverter;

            if (converter != null && converter.CanWrite)
            {
                SerializeConvertable(writer, converter, value, valueContract, containerContract, containerProperty);
                return;
            }

            switch (valueContract.ContractType)
            {
            case JsonContractType.Object:
                SerializeObject(writer, value, (JsonObjectContract)valueContract, member, containerContract, containerProperty);
                break;

            case JsonContractType.Array:
                JsonArrayContract arrayContract = (JsonArrayContract)valueContract;
                if (!arrayContract.IsMultidimensionalArray)
                {
                    SerializeList(writer, (IEnumerable)value, arrayContract, member, containerContract, containerProperty);
                }
                else
                {
                    SerializeMultidimensionalArray(writer, (Array)value, arrayContract, member, containerContract, containerProperty);
                }
                break;

            case JsonContractType.Primitive:
                SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, containerContract, containerProperty);
                break;

            case JsonContractType.String:
                SerializeString(writer, value, (JsonStringContract)valueContract);
                break;

            case JsonContractType.Dictionary:
                JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract;
                SerializeDictionary(writer, (value is IDictionary dictionary) ? dictionary : dictionaryContract.CreateWrapper(value), dictionaryContract, member, containerContract, containerProperty);
                break;

#if HAVE_DYNAMIC
            case JsonContractType.Dynamic:
                SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract, member, containerContract, containerProperty);
                break;
#endif
#if HAVE_BINARY_SERIALIZATION
            case JsonContractType.Serializable:
                SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, containerContract, containerProperty);
                break;
#endif
            case JsonContractType.Linq:
                ((JToken)value).WriteTo(writer, Serializer.Converters.ToArray());
                break;
            }
        }
        private string GetPropertyName(JsonWriter writer, object name, JsonContract contract, out bool escape)
        {
            if (contract.ContractType == JsonContractType.Primitive)
            {
                JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract;
                switch (primitiveContract.TypeCode)
                {
                case PrimitiveTypeCode.DateTime:
                case PrimitiveTypeCode.DateTimeNullable:
                {
                    DateTime dt = DateTimeUtils.EnsureDateTime((DateTime)name, writer.DateTimeZoneHandling);

                    escape = false;
                    StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
                    DateTimeUtils.WriteDateTimeString(sw, dt, writer.DateFormatHandling, writer.DateFormatString, writer.Culture);
                    return(sw.ToString());
                }

#if HAVE_DATE_TIME_OFFSET
                case PrimitiveTypeCode.DateTimeOffset:
                case PrimitiveTypeCode.DateTimeOffsetNullable:
                {
                    escape = false;
                    StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
                    DateTimeUtils.WriteDateTimeOffsetString(sw, (DateTimeOffset)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture);
                    return(sw.ToString());
                }
#endif
                case PrimitiveTypeCode.Double:
                case PrimitiveTypeCode.DoubleNullable:
                {
                    double d = (double)name;

                    escape = false;
                    return(d.ToString("R", CultureInfo.InvariantCulture));
                }

                case PrimitiveTypeCode.Single:
                case PrimitiveTypeCode.SingleNullable:
                {
                    float f = (float)name;

                    escape = false;
                    return(f.ToString("R", CultureInfo.InvariantCulture));
                }

                default:
                {
                    escape = true;

                    if (primitiveContract.IsEnum && EnumUtils.TryToString(primitiveContract.NonNullableUnderlyingType, name, null, out string enumName))
                    {
                        return(enumName);
                    }

                    return(Convert.ToString(name, CultureInfo.InvariantCulture));
                }
                }
            }
            else if (TryConvertToString(name, name.GetType(), out string propertyName))
            {
                escape = true;
                return(propertyName);
            }
            else
            {
                escape = true;
                return(name.ToString());
            }
        }
        private void SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
        {
            object underlyingDictionary = values is IWrappedDictionary wrappedDictionary ? wrappedDictionary.UnderlyingDictionary : values;

            OnSerializing(writer, contract, underlyingDictionary);
            _serializeStack.Add(underlyingDictionary);

            WriteObjectStart(writer, underlyingDictionary, contract, member, collectionContract, containerProperty);

            if (contract.ItemContract == null)
            {
                contract.ItemContract = Serializer._contractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object));
            }

            if (contract.KeyContract == null)
            {
                contract.KeyContract = Serializer._contractResolver.ResolveContract(contract.DictionaryKeyType ?? typeof(object));
            }

            int initialDepth = writer.Top;

            // Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
            IDictionaryEnumerator e = values.GetEnumerator();

            try
            {
                while (e.MoveNext())
                {
                    DictionaryEntry entry = e.Entry;

                    string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out bool escape);

                    propertyName = (contract.DictionaryKeyResolver != null)
                        ? contract.DictionaryKeyResolver(propertyName)
                        : propertyName;

                    try
                    {
                        object       value         = entry.Value;
                        JsonContract valueContract = contract.FinalItemContract ?? GetContractSafe(value);

                        if (ShouldWriteReference(value, null, valueContract, contract, member))
                        {
                            writer.WritePropertyName(propertyName, escape);
                            WriteReference(writer, value);
                        }
                        else
                        {
                            if (!CheckForCircularReference(writer, value, null, valueContract, contract, member))
                            {
                                continue;
                            }

                            writer.WritePropertyName(propertyName, escape);

                            SerializeValue(writer, value, valueContract, null, contract, member);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (IsErrorHandled(underlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex))
                        {
                            HandleError(writer, initialDepth);
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }
            finally
            {
                (e as IDisposable)?.Dispose();
            }

            writer.WriteEndObject();

            _serializeStack.RemoveAt(_serializeStack.Count - 1);

            OnSerialized(writer, contract, underlyingDictionary);
        }