private void PopulateSchema(JSchema schema, JsonContract contract, JsonProperty memberProperty, Required valueRequired)
        {
            Type nonNullableUnderlyingType = GetNonNullableUnderlyingType(contract);

            schema.Title       = GetTitle(nonNullableUnderlyingType, memberProperty);
            schema.Description = GetDescription(nonNullableUnderlyingType, memberProperty);

            JsonConverter converter;

            if (contract.Converter != null && contract.Converter.CanWrite)
            {
                converter = contract.Converter;
            }
            else if (contract.InternalConverter != null && contract.InternalConverter.CanWrite)
            {
                converter = contract.InternalConverter;
            }
            else
            {
                converter = null;
            }

            if (converter != null)
            {
                schema.Type = null;
            }
            else
            {
                switch (contract)
                {
                case JsonObjectContract objectContract:
                    if (nonNullableUnderlyingType == typeof(object))
                    {
                        PopulatePrimativeSchema(schema, objectContract, memberProperty, valueRequired);
                    }
                    else
                    {
                        if (schema.Id == null)
                        {
                            schema.Id = GetTypeId(nonNullableUnderlyingType, false);
                        }

                        schema.Type = AddNullType(JSchemaType.Object, valueRequired);
                        GenerateObjectSchema(schema, nonNullableUnderlyingType, objectContract);
                    }
                    break;

                case JsonArrayContract arrayContract:
                    if (schema.Id == null)
                    {
                        schema.Id = GetTypeId(nonNullableUnderlyingType, false);
                    }

                    schema.Type         = AddNullType(JSchemaType.Array, valueRequired);
                    schema.MinimumItems = AttributeHelpers.GetMinLength(memberProperty);
                    schema.MaximumItems = AttributeHelpers.GetMaxLength(memberProperty);
                    schema.UniqueItems  = ReflectionUtils.IsISetType(nonNullableUnderlyingType) || AttributeHelpers.GetUniqueItems(memberProperty);

                    JsonArrayAttribute arrayAttribute = ReflectionUtils.GetAttribute <JsonArrayAttribute>(nonNullableUnderlyingType);

                    Required?required = null;
                    if (arrayAttribute != null && !arrayAttribute.AllowNullItems)
                    {
                        required = Required.Always;
                    }

                    Type collectionItemType = ReflectionUtils.GetCollectionItemType(nonNullableUnderlyingType);
                    if (collectionItemType != null)
                    {
                        schema.Items.Add(GenerateInternal(collectionItemType, required, null, arrayContract, null));
                    }
                    break;

                case JsonStringContract stringContract:
                    JSchemaType schemaType = (!ReflectionUtils.IsNullable(stringContract.UnderlyingType))
                            ? JSchemaType.String
                            : AddNullType(JSchemaType.String, valueRequired);

                    schema.Type          = schemaType;
                    schema.MinimumLength = AttributeHelpers.GetMinLength(memberProperty);
                    schema.MaximumLength = AttributeHelpers.GetMaxLength(memberProperty);
                    break;

                case JsonPrimitiveContract primitiveContract:
                    PopulatePrimativeSchema(schema, primitiveContract, memberProperty, valueRequired);
                    break;

                case JsonDictionaryContract dictionaryContract:
                    schema.Type = AddNullType(JSchemaType.Object, valueRequired);
                    schema.MinimumProperties = AttributeHelpers.GetMinLength(memberProperty);
                    schema.MaximumProperties = AttributeHelpers.GetMaxLength(memberProperty);

                    ReflectionUtils.GetDictionaryKeyValueTypes(nonNullableUnderlyingType, out Type keyType, out Type valueType);

                    if (keyType != null)
                    {
                        JsonContract keyContract = _generator.ContractResolver.ResolveContract(keyType);

                        // can be converted to a string
                        if (keyContract is JsonPrimitiveContract)
                        {
                            schema.AdditionalProperties = GenerateInternal(valueType, _generator.DefaultRequired, null, dictionaryContract, null);
                        }
                    }
                    break;

#if !PORTABLE || NETSTANDARD1_3 || NETSTANDARD2_0
                case JsonISerializableContract serializableContract:
                    if (schema.Id == null)
                    {
                        schema.Id = GetTypeId(nonNullableUnderlyingType, false);
                    }

                    schema.Type = AddNullType(JSchemaType.Object, valueRequired);
                    schema.AllowAdditionalProperties = true;
                    break;
#endif
#if !NET35
                case JsonDynamicContract dynamicContract:
#endif
                case JsonLinqContract linqContract:
                    schema.Type = null;
                    break;

                default:
                    throw new JSchemaException("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract));
                }
            }
        }