func (b definitionBuilder) isPrimitiveType(modelName string, modelKind reflect.Kind) bool {
	switch modelKind {
	case reflect.Bool:
		return true
	case reflect.Float32, reflect.Float64,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return true
	case reflect.String:
		return true
	}
	if len(modelName) == 0 {
		return false
	}
	return strings.Contains("time.Time time.Duration json.Number", modelName)
}
see also https://golang.org/ref/spec#Numeric_types
package restfulspec
import (
	"encoding/json"
	"reflect"
	"strings"
	"github.com/go-openapi/spec"
)
type definitionBuilder struct {
	Definitions spec.Definitions
	Config      Config
}
type Documented interface {
	SwaggerDoc() map[string]string
}
func getDocFromMethodSwaggerDoc2(model reflect.Type) map[string]string {
	if docable, ok := reflect.New(model).Elem().Interface().(Documented); ok {
		return docable.SwaggerDoc()
	}
	return make(map[string]string)
}
func (b definitionBuilder) addModelFrom(sample interface{}) {
	b.addModel(reflect.TypeOf(sample), "")
}
func (b definitionBuilder) addModel(st reflect.Type, nameOverride string) *spec.Schema {
	if st.Kind() == reflect.Ptr {
		st = st.Elem()
	}
	if b.isSliceOrArrayType(st.Kind()) {
		st = st.Elem()
	}
	modelName := keyFrom(st, b.Config)
	if nameOverride != "" {
		modelName = nameOverride
	}
	if b.isPrimitiveType(modelName, st.Kind()) {
		return nil
	}
	if b.isByteArrayType(st) {
		return nil
	}
	if _, ok := b.Definitions[modelName]; ok {
		return nil
	}
	sm := spec.Schema{
		SchemaProps: spec.SchemaProps{
			Required:   []string{},
			Properties: map[string]spec.Schema{},
		},
	}
	b.Definitions[modelName] = sm
	if st.Kind() == reflect.Map {
		_, sm = b.buildMapType(st, "value", modelName)
		b.Definitions[modelName] = sm
		return &sm
	}
	if st.Kind() != reflect.Struct {
		return &sm
	}
	fullDoc := getDocFromMethodSwaggerDoc2(st)
	modelDescriptions := []string{}
	for i := 0; i < st.NumField(); i++ {
		field := st.Field(i)
		jsonName, modelDescription, prop := b.buildProperty(field, &sm, modelName)
		if len(modelDescription) > 0 {
			modelDescriptions = append(modelDescriptions, modelDescription)
		}
		if len(jsonName) != 0 {
			if fieldDoc, ok := fullDoc[jsonName]; ok {
				prop.Description = fieldDoc
			}
			if b.isPropertyRequired(field) {
				sm.Required = append(sm.Required, jsonName)
			}
			sm.Properties[jsonName] = prop
		}
	}
	if modelDoc, ok := fullDoc[""]; ok {
		sm.Description = modelDoc
	} else if len(modelDescriptions) != 0 {
		sm.Description = strings.Join(modelDescriptions, "\n")
	}
	sm.ID = ""
	b.Definitions[modelName] = sm
	return &sm
}
func (b definitionBuilder) isPropertyRequired(field reflect.StructField) bool {
	required := true
	if optionalTag := field.Tag.Get("optional"); optionalTag == "true" {
		return false
	}
	if jsonTag := field.Tag.Get("json"); jsonTag != "" {
		s := strings.Split(jsonTag, ",")
		if len(s) > 1 && s[1] == "omitempty" {
			return false
		}
	}
	return required
}
func (b definitionBuilder) buildProperty(field reflect.StructField, model *spec.Schema, modelName string) (jsonName, modelDescription string, prop spec.Schema) {
	jsonName = b.jsonNameOfField(field)
	if len(jsonName) == 0 {
		return "", "", prop
	}
	if field.Name == "XMLName" && field.Type.String() == "xml.Name" {
		return "", "", prop
	}
	if tag := field.Tag.Get("modelDescription"); tag != "" {
		modelDescription = tag
	}
	setPropertyMetadata(&prop, field)
	if prop.Type != nil {
		return jsonName, modelDescription, prop
	}
	fieldType := field.Type
	marshalerType := reflect.TypeOf((*json.Marshaler)(nil)).Elem()
	if fieldType.Implements(marshalerType) {
		var pType = "string"
		if prop.Type == nil {
			prop.Type = []string{pType}
		}
		if prop.Format == "" {
			prop.Format = b.jsonSchemaFormat(keyFrom(fieldType, b.Config), fieldType.Kind())
		}
		return jsonName, modelDescription, prop
	}
	if jsonTag := field.Tag.Get("json"); jsonTag != "" {
		s := strings.Split(jsonTag, ",")
		if len(s) > 1 && s[1] == "string" {
			stringt := "string"
			prop.Type = []string{stringt}
			return jsonName, modelDescription, prop
		}
	}
	fieldKind := fieldType.Kind()
	switch {
	case fieldKind == reflect.Struct:
		jsonName, prop := b.buildStructTypeProperty(field, jsonName, model)
		return jsonName, modelDescription, prop
	case b.isSliceOrArrayType(fieldKind):
		jsonName, prop := b.buildArrayTypeProperty(field, jsonName, modelName)
		return jsonName, modelDescription, prop
	case fieldKind == reflect.Ptr:
		jsonName, prop := b.buildPointerTypeProperty(field, jsonName, modelName)
		return jsonName, modelDescription, prop
	case fieldKind == reflect.Map:
		jsonName, prop := b.buildMapTypeProperty(field, jsonName, modelName)
		return jsonName, modelDescription, prop
	}
	fieldTypeName := keyFrom(fieldType, b.Config)
	if b.isPrimitiveType(fieldTypeName, fieldKind) {
		mapped := b.jsonSchemaType(fieldTypeName, fieldKind)
		prop.Type = []string{mapped}
		prop.Format = b.jsonSchemaFormat(fieldTypeName, fieldKind)
		return jsonName, modelDescription, prop
	}
	modelType := keyFrom(fieldType, b.Config)
	prop.Ref = spec.MustCreateRef("#/definitions/" + modelType)
	if fieldType.Name() == "" {  
		nestedTypeName := modelName + "." + jsonName
		prop.Ref = spec.MustCreateRef("#/definitions/" + nestedTypeName)
		b.addModel(fieldType, nestedTypeName)
	}
	return jsonName, modelDescription, prop
}
func hasNamedJSONTag(field reflect.StructField) bool {
	parts := strings.Split(field.Tag.Get("json"), ",")
	if len(parts) == 0 {
		return false
	}
	for _, s := range parts[1:] {
		if s == "inline" {
			return false
		}
	}
	return len(parts[0]) > 0
}
func (b definitionBuilder) buildStructTypeProperty(field reflect.StructField, jsonName string, model *spec.Schema) (nameJson string, prop spec.Schema) {
	setPropertyMetadata(&prop, field)
	fieldType := field.Type
	if len(fieldType.Name()) == 0 {
		anonType := model.ID + "." + jsonName
		b.addModel(fieldType, anonType)
		prop.Ref = spec.MustCreateRef("#/definitions/" + anonType)
		return jsonName, prop
	}
	if field.Name == fieldType.Name() && field.Anonymous && !hasNamedJSONTag(field) {
		sub := definitionBuilder{b.Definitions, b.Config}
		sub.addModel(fieldType, "")
		subKey := keyFrom(fieldType, b.Config)
		subModel, _ := sub.Definitions[subKey]
		for k, v := range subModel.Properties {
			model.Properties[k] = v
			required := false
			for _, each := range subModel.Required {
				if k == each {
					required = true
					break
				}
			}
			if required {
				model.Required = append(model.Required, k)
			}
		}
		for key, sub := range sub.Definitions {
			if key != subKey {
				if _, ok := b.Definitions[key]; !ok {
					b.Definitions[key] = sub
				}
			}
		}
		return "", prop
	}
	b.addModel(fieldType, "")
	var pType = keyFrom(fieldType, b.Config)
	prop.Ref = spec.MustCreateRef("#/definitions/" + pType)
	return jsonName, prop
}
func (b definitionBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
	setPropertyMetadata(&prop, field)
	fieldType := field.Type
	if fieldType.Elem().Kind() == reflect.Uint8 {
		stringt := "string"
		prop.Type = []string{stringt}
		return jsonName, prop
	}
	var pType = "array"
	prop.Type = []string{pType}
	isPrimitive := b.isPrimitiveType(fieldType.Elem().Name(), fieldType.Elem().Kind())
	elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
	prop.Items = &spec.SchemaOrArray{
		Schema: &spec.Schema{},
	}
	if isPrimitive {
		mapped := b.jsonSchemaType(elemTypeName, fieldType.Elem().Kind())
		prop.Items.Schema.Type = []string{mapped}
	} else {
		prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
	}
	if fieldType.Elem().Kind() == reflect.Ptr {
		fieldType = fieldType.Elem()
	}
	if !isPrimitive {
		b.addModel(fieldType.Elem(), elemTypeName)
	}
	return jsonName, prop
}
func (b definitionBuilder) buildMapTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
	nameJson, prop = b.buildMapType(field.Type, jsonName, modelName)
	setPropertyMetadata(&prop, field)
	return nameJson, prop
}
func (b definitionBuilder) buildMapType(mapType reflect.Type, jsonName, modelName string) (nameJson string, prop spec.Schema) {
	var pType = "object"
	prop.Type = []string{pType}
	if mapType.Elem().Kind().String() != "interface" {
		isSlice := b.isSliceOrArrayType(mapType.Elem().Kind())
		if isSlice && !b.isByteArrayType(mapType.Elem()) {
			mapType = mapType.Elem()
		}
		isPrimitive := b.isPrimitiveType(mapType.Elem().Name(), mapType.Elem().Kind())
		elemTypeName := b.getElementTypeName(modelName, jsonName, mapType.Elem())
		prop.AdditionalProperties = &spec.SchemaOrBool{
			Schema: &spec.Schema{},
		}
		if b.isByteArrayType(mapType.Elem()) {
			prop.AdditionalProperties.Schema.Type = []string{"string"}
		} else {
			if isSlice {
				var item *spec.Schema
				if isPrimitive {
					mapped := b.jsonSchemaType(elemTypeName, mapType.Kind())
					item = &spec.Schema{}
					item.Type = []string{mapped}
					item.Format = b.jsonSchemaFormat(elemTypeName, mapType.Kind())
				} else {
					item = spec.RefProperty("#/definitions/" + elemTypeName)
				}
				prop.AdditionalProperties.Schema = spec.ArrayProperty(item)
			} else if isPrimitive {
				mapped := b.jsonSchemaType(elemTypeName, mapType.Elem().Kind())
				prop.AdditionalProperties.Schema.Type = []string{mapped}
			} else {
				prop.AdditionalProperties.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
			}
			if mapType.Elem().Kind() == reflect.Ptr {
				mapType = mapType.Elem()
			}
			if !isPrimitive {
				b.addModel(mapType.Elem(), elemTypeName)
			}
		}
	}
	return jsonName, prop
}
func (b definitionBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
	setPropertyMetadata(&prop, field)
	fieldType := field.Type
	if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array {
		var pType = "array"
		prop.Type = []string{pType}
		isPrimitive := b.isPrimitiveType(fieldType.Elem().Elem().Name(), fieldType.Elem().Elem().Kind())
		elemName := b.getElementTypeName(modelName, jsonName, fieldType.Elem().Elem())
		prop.Items = &spec.SchemaOrArray{
			Schema: &spec.Schema{},
		}
		if isPrimitive {
			primName := b.jsonSchemaType(elemName, fieldType.Elem().Elem().Kind())
			prop.Items.Schema.Type = []string{primName}
		} else {
			prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemName)
		}
		if !isPrimitive {
			b.addModel(fieldType.Elem().Elem(), elemName)
		}
	} else {
		fieldTypeName := keyFrom(fieldType.Elem(), b.Config)
		isPrimitive := b.isPrimitiveType(fieldTypeName, fieldType.Elem().Kind())
		var pType = b.jsonSchemaType(fieldTypeName, fieldType.Elem().Kind())  
		if isPrimitive {
			prop.Type = []string{pType}
			prop.Format = b.jsonSchemaFormat(fieldTypeName, fieldType.Elem().Kind())
			return jsonName, prop
		}
		prop.Ref = spec.MustCreateRef("#/definitions/" + pType)
		elemName := ""
		if fieldType.Elem().Name() == "" {
			elemName = modelName + "." + jsonName
			prop.Ref = spec.MustCreateRef("#/definitions/" + elemName)
		}
		if !isPrimitive {
			b.addModel(fieldType.Elem(), elemName)
		}
	}
	return jsonName, prop
}
func (b definitionBuilder) getElementTypeName(modelName, jsonName string, t reflect.Type) string {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Name() == "" {
		return modelName + "." + jsonName
	}
	return keyFrom(t, b.Config)
}
func keyFrom(st reflect.Type, cfg Config) string {
	key := st.String()
	if cfg.ModelTypeNameHandler != nil {
		if name, ok := cfg.ModelTypeNameHandler(st); ok {
			key = name
		}
	}
	if len(st.Name()) == 0 {  
		key = strings.TrimPrefix(key, "[]")
		key = strings.Replace(key, "[]", "||", -1)
	}
	return key
}
func (b definitionBuilder) isSliceOrArrayType(t reflect.Kind) bool {
	return t == reflect.Slice || t == reflect.Array
}
func (b definitionBuilder) isByteArrayType(t reflect.Type) bool {
	return (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) &&
		t.Elem().Kind() == reflect.Uint8
}