ca.oson.json.Oson.java Source code

Java tutorial

Introduction

Here is the source code for ca.oson.json.Oson.java

Source

/*******************************************************************************
 * Copyright (c) 2016- Oson.ca
 * @author   David Ruifang He
 * @email   osonus@gmail.com
 *
 * All rights reserved.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * You may elect to redistribute this code, under the condition that you may not
 * modify this copyright header
 *******************************************************************************/
package ca.oson.json;

import java.beans.Expression;
import java.beans.Statement;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.validation.constraints.Size;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty.Access;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.FieldNamingStrategy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until;

import ca.oson.json.Oson.BOOLEAN;
import ca.oson.json.function.*;
import ca.oson.json.util.*;

/**
 * Convert java object to and from Json String
 *
 * @author   David Ruifang He
 * Date   June 15, 2016
 */
public class Oson {
    private static final String mixin = "MixIn";

    public static enum JSON_PROCESSOR {
        JACKSON, // use Jacksopn's implementation
        GSON, // use google's gson implementation
        OSON // Oson Json processor in Java
    };

    // used by annotations
    public static enum BOOLEAN {
        FALSE, TRUE, NONE, // the same as null
        BOTH; // the same as all

        public static Boolean valueOf(BOOLEAN bool) {
            switch (bool) {
            case FALSE:
                return false;
            case TRUE:
                return true;
            case NONE:
                return null;
            default:
                return null;
            }
        }

        public Boolean value() {
            return valueOf(this);
        }
    };

    // used by annotations
    public static enum ENUM_TYPE {
        /** Persist enumerated type property or field as an integer */
        ORDINAL,

        /** Persist enumerated type property or field as a string */
        STRING,

        NONE; // the same as null

        public static EnumType valueOf(ENUM_TYPE en) {
            switch (en) {
            case ORDINAL:
                return EnumType.ORDINAL;
            case STRING:
                return EnumType.STRING;
            default:
                return null;
            }
        }

        public EnumType value() {
            return valueOf(this);
        }
    };

    public static enum FIELD_NAMING {
        FIELD, // someField_name: use field name of a class/object, original field name
        LOWER, // simple lower case
        UPPER, // simply upper case
        CAMELCASE, // someFieldName: convert underscore to camelcase, first leter lower case
        UPPER_CAMELCASE, // SomeFieldName: first letter upper case
        UNDERSCORE_CAMELCASE, // some_Field_Name
        UNDERSCORE_UPPER_CAMELCASE, // Some_Field_Name
        UNDERSCORE_LOWER, // some_field_name: convert camelcase to underscore, lower case
        UNDERSCORE_UPPER, // SOME_FIELD_NAME: convert camelcase to underscore, upper case
        SPACE_CAMELCASE, // some Field Name
        SPACE_UPPER_CAMELCASE, // Some Field Name
        SPACE_LOWER, // some field name
        SPACE_UPPER, // SOME FIELD NAME
        DASH_CAMELCASE, // some-Field-Name
        DASH_UPPER_CAMELCASE, // Some-Field-Name
        DASH_LOWER, // some-field-name
        DASH_UPPER // SOME-FIELD-NAME
    };

    public static enum JSON_INCLUDE {
        ALWAYS, NON_NULL, NON_EMPTY, NON_DEFAULT, DEFAULT, NONE // means it has no value
    }

    public static enum MODIFIER {
        Public, Protected, Package, // Package Private, no modifier
        Private, Abstract, Final, Interface, Native, Static, Strict, Synchronized, Transient, Volatile, Synthetic, All, None
    }

    public static class FieldData<T, E, R> implements Cloneable {
        // enclosing object, such as a field or component in a enclosing class
        public T enclosingObj;
        private Class<T> enclosingtype = null;

        // initial default value
        public Object defaultValue = null;

        // object to return, might be created from the defaultValue
        public Class<R> returnType;
        public R returnObj = null;
        // private Class<E> componentType = null;

        // value coming in to be processed
        private Field field;
        transient public E valueToProcess;

        // specifically map data to process, mainly for deserializing json to class object
        //public Map<String, Object> mapToProcess;

        // keeps type information
        //public ComponentType componentType = null;
        public boolean json2Java = true;

        // extra field information
        transient private Method getter;
        transient private Method setter;

        transient private FieldMapper fieldMapper = null;
        public ClassMapper classMapper = null;

        // various configuration attributes
        Boolean required = false;
        JSON_INCLUDE defaultType = JSON_INCLUDE.NONE;

        EnumType enumType = null;
        Integer length = null; // Default: 255
        Integer precision = null;
        Integer scale = null; // Default: 0
        Long min = null; // default (int) 0;
        Long max = null; // default (int) 2147483647;

        public Boolean jsonRawValue = null;

        private int level = 0;
        private Set set;
        // for internal use only
        public boolean doubleQuote = false;
        transient private ComponentType componentType;
        transient public boolean isMapValue = false;

        public Integer getLength() {
            if (length == null && classMapper != null) {
                length = classMapper.length;
            }

            return length;
        }

        public void incrLevel() {
            this.level++;
        }

        public void descLevel() {
            this.level--;
        }

        //true if this set did not already contain the specified element
        public boolean goAhead(int hash) {
            return set.add(hash); // || level < 2;
        }

        public boolean isJsonRawValue() {
            if (level == 0 || (jsonRawValue != null && jsonRawValue)) {
                return true;
            } else {
                return false;
            }
        }

        public boolean required() {
            return (this.required != null && this.required);
        }

        public Class getEnclosingtype() {
            if (enclosingtype == null && enclosingObj != null) {
                enclosingtype = (Class<T>) enclosingObj.getClass();
            }

            return enclosingtype;
        }

        public FieldData(T enclosingObj, Field field, E valueToProcess, Class<R> returnType, boolean json2Java,
                FieldMapper fieldMapper) {
            this(enclosingObj, field, valueToProcess, returnType, json2Java, fieldMapper, 0,
                    Collections.synchronizedSet(new HashSet()));
        }

        public FieldData(T enclosingObj, Field field, E valueToProcess, Class<R> returnType, boolean json2Java,
                FieldMapper fieldMapper, int level, Set set) {
            this.enclosingObj = enclosingObj; // enclosing object
            this.field = field;
            this.valueToProcess = valueToProcess;
            this.returnType = returnType;
            this.json2Java = json2Java;
            this.fieldMapper = fieldMapper;

            this.required = fieldMapper.required;
            this.defaultType = fieldMapper.defaultType;
            this.length = fieldMapper.length;
            this.precision = fieldMapper.precision;
            this.scale = fieldMapper.scale;
            this.min = fieldMapper.min;
            this.max = fieldMapper.max;
            this.defaultValue = fieldMapper.defaultValue;
            this.jsonRawValue = fieldMapper.jsonRawValue;

            this.level = level;
            this.set = set;
        }

        public FieldData(E valueToProcess, Class<R> returnType) {
            this.valueToProcess = valueToProcess;
            this.returnType = returnType;
            this.level = 0;
            this.set = Collections.synchronizedSet(new HashSet());
        }

        // deserialize2Object(Map<String, Object> mapToProcess, Class<T> valueType, T obj)
        public FieldData(E valueToProcess, Class<R> returnType, R returnObj, boolean json2Java) {
            this.valueToProcess = valueToProcess;
            this.returnType = returnType;
            this.json2Java = json2Java;
            this.returnObj = returnObj;
            this.level = 0;
            this.set = Collections.synchronizedSet(new HashSet());
        }

        public FieldData(E valueToProcess, Class<R> returnType, boolean json2Java) {
            this.valueToProcess = valueToProcess;
            this.returnType = returnType;
            this.json2Java = json2Java;
            this.returnObj = returnObj;
            this.level = 0;
            this.set = Collections.synchronizedSet(new HashSet());
        }

        public FieldData(E valueToProcess, boolean json2Java) {
            this.valueToProcess = valueToProcess; // value to interpret
            this.json2Java = json2Java;
            this.level = 0;
            this.set = Collections.synchronizedSet(new HashSet());
        }

        public FieldData(E valueToProcess, Class<R> returnType, boolean json2Java, int level, Set set) {
            this.valueToProcess = valueToProcess;
            this.returnType = returnType;
            this.json2Java = json2Java;
            this.level = level;
            this.set = set;
        }

        private FieldData() {
        }

        public Class getEnclosingType() {
            if (enclosingtype != null) {
                return enclosingtype;
            }

            if (enclosingObj != null) {
                enclosingtype = (Class<T>) enclosingObj.getClass();
            }
            if (enclosingtype == null && classMapper != null) {
                enclosingtype = classMapper.getType();
            }
            if (enclosingtype == null && fieldMapper != null) {
                enclosingtype = fieldMapper.getType();
            }

            return enclosingtype;
        }

        public String getDefaultName() {
            if (fieldMapper != null) {
                if (fieldMapper.java != null) {
                    return fieldMapper.java;
                } else {
                    return fieldMapper.json;
                }
            }

            return null;
        }

        public Function getFunction() {
            if (this.json2Java) {
                return getDeserializer();
            } else {
                return getSerializer();
            }
        }

        public Function getDeserializer() {
            if (fieldMapper != null && fieldMapper.deserializer != null) {
                return fieldMapper.deserializer;
            }
            if (classMapper != null && classMapper.deserializer != null) {
                return classMapper.deserializer;
            }

            return null;
        }

        public Function getSerializer() {
            if (fieldMapper != null && fieldMapper.serializer != null) {
                return fieldMapper.serializer;
            }
            if (classMapper != null && classMapper.serializer != null) {
                return classMapper.serializer;
            }

            return null;
        }

        public Integer getScale() {
            if (scale == null && classMapper != null) {
                scale = classMapper.scale;
            }

            return scale;
        }

        public Integer getPrecision() {
            if (precision == null && classMapper != null) {
                precision = classMapper.precision;
            }

            return precision;
        }

        public Long getMin() {
            if (min == null && classMapper != null) {
                min = classMapper.min;
            }

            if (max != null && returnType != null) {
                if ((returnType == Character.class || returnType == char.class)) {
                    if (min.intValue() < Character.MIN_CODE_POINT) {
                        min = (long) Character.MIN_CODE_POINT;
                    }

                } else if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()) {
                    if (returnType == Short.class || returnType == short.class) {
                        if (min.intValue() < Short.MIN_VALUE) {
                            min = (long) Short.MIN_VALUE;
                        }

                    } else if (returnType == Byte.class || returnType == byte.class) {
                        if (min.intValue() < Byte.MIN_VALUE) {
                            min = (long) Byte.MIN_VALUE;
                        }
                    }
                }
            }

            return min;
        }

        public Long getMax() {
            if (max == null && classMapper != null) {
                max = classMapper.max;
            }

            if (max != null && returnType != null) {
                if ((returnType == Character.class || returnType == char.class)) {
                    if (max.intValue() > Character.MAX_CODE_POINT) {
                        max = (long) Character.MAX_CODE_POINT;
                    }

                } else if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()) {
                    if (returnType == Short.class || returnType == short.class) {
                        if (max.intValue() > Short.MAX_VALUE) {
                            max = (long) Short.MAX_VALUE;
                        }

                    } else if (returnType == Byte.class || returnType == byte.class) {
                        if (max.intValue() > Byte.MAX_VALUE) {
                            max = (long) Byte.MAX_VALUE;
                        }
                    }
                }
            }

            return max;
        }

        public Object getDefaultValue() {

            if (defaultValue != null) {
                return defaultValue;
            }

            if (enclosingObj != null) {
                if (getter == null) {
                    if (field != null) {
                        String name = field.getName();

                        String getterName = "get" + StringUtil.capitalize(name);
                        try {
                            getter = enclosingObj.getClass().getMethod(getterName, null);

                        } catch (NoSuchMethodException | IllegalArgumentException e) {
                            // e.printStackTrace();
                        }
                    }
                }

                if (getter != null) {
                    defaultValue = ObjectUtil.getMethodValue(enclosingObj, getter);
                }
            }

            if (defaultValue == null && classMapper != null) {
                defaultValue = classMapper.defaultValue;
            }

            return defaultValue;
        }

        public EnumType getEnumType() {
            if (enumType == null && classMapper != null) {
                enumType = classMapper.enumType;
            }

            return enumType;
        }

        private DateFormat dateFormat = null;

        public DateFormat getDateFormat() {
            if (dateFormat != null) {
                return dateFormat;
            }
            if (fieldMapper != null) {
                dateFormat = fieldMapper.getDateFormat();
            }
            if (dateFormat == null && classMapper != null) {
                dateFormat = classMapper.getDateFormat();
            }

            if (dateFormat == null) {
                dateFormat = new SimpleDateFormat(DefaultValue.simpleDateFormat());
            }

            return dateFormat;
        }

        public Boolean getDate2Long() {
            if (fieldMapper != null) {
                if (fieldMapper.date2Long != null) {
                    return fieldMapper.date2Long;
                }
            }
            if (classMapper != null) {
                if (classMapper.date2Long != null) {
                    return classMapper.date2Long;
                }
            }

            return null;
        }

        public Class getComponentType(String jsonClassType) {
            Class<E> componentType = (Class<E>) returnType.getComponentType();
            if (componentType == null) {
                if (componentType == null && valueToProcess != null
                        && Collection.class.isAssignableFrom(valueToProcess.getClass())) {
                    Collection<E> values = (Collection<E>) valueToProcess;

                    componentType = CollectionArrayTypeGuesser.guessElementType(values,
                            (Class<Collection<E>>) values.getClass(), jsonClassType);
                }

                if (componentType == null && valueToProcess != null
                        && Map.class.isAssignableFrom(valueToProcess.getClass())) {
                    Map<String, Object> values = (Map) valueToProcess;
                    componentType = CollectionArrayTypeGuesser.guessElementType(values, returnType, jsonClassType);
                }

                if (componentType == null && valueToProcess != null && returnType.isArray()) {
                    E[] array = (E[]) valueToProcess;
                    componentType = CollectionArrayTypeGuesser.guessElementType(array, returnType);
                }

                if (componentType == null)
                    componentType = (Class<E>) Object.class;

                if (componentType.isPrimitive()) {
                    componentType = ObjectUtil.getObjectType(componentType);
                }
            }

            return componentType;
        }

        protected FieldData clone() throws CloneNotSupportedException {
            try {
                FieldData clone = new FieldData();
                CopyObjects.copy(this, clone, true);
                clone.valueToProcess = this.valueToProcess;

                return clone;
            } catch (Exception e) {
                return null;
            }
        }
    }

    // holding all states inside these 3 objects
    private Options options = new Options();
    private ObjectMapper jackson = null;
    private Gson gson = null;

    private enum METHOD {
        SET(0), GET(1), OTHER(2);
        private int value;

        private METHOD(int value) {
            this.value = value;
        }
    }

    // there are caching mechanism, should be safe to be shared across
    private static Map<Class, Field[]> cachedFields = new ConcurrentHashMap<>();
    private static Map<String, Map<String, Method>[]> cachedMethods = new ConcurrentHashMap<>();
    private static Map<Class, ComponentType> cachedComponentTypes = new ConcurrentHashMap<>();
    private Class masterClass = null;

    ////////////////////////////////////////////////////////////////////////////////
    // END OF variables and class definition
    ////////////////////////////////////////////////////////////////////////////////

    public Oson() {
        ObjectUtil.getJSONObject(null);
    }

    public Oson(JSONObject json) {
        this();
        configure(json);
    }

    public Oson(String json) {
        this();
        configure(json);
    }

    public Oson(Object[] array) {
        this();
        configure(array);
    }

    public Oson(Map<String, Object> map) {
        this();
        configure(map);
    }

    public Oson(Options options) {
        this();
        configure(options);
    }

    public Oson configure(JSONObject json) {
        return configure(json.toString());
    }

    public Oson configure(String json) {
        Options options = this.fromJson(json, Options.class);

        return configure(options);
    }

    public Oson configure(Object[] array) {
        Map<String, Object> map = ArrayToJsonMap.array2Map(array);

        return configure(map);
    }

    public Oson configure(Map<String, Object> map) {
        Options options = this.deserialize2Object(map, Options.class, null);

        return configure(options);
    }

    public Oson configure(Options options) {
        if (options != null) {
            if (this.options == null) {
                this.options = new Options();
            }
            CopyObjects.copy(options, this.options, false);

            reset();
        }
        return this;
    }

    private JSON_PROCESSOR getJsonProcessor() {
        return options.getJsonProcessor();
    }

    public Oson setJsonProcessor(JSON_PROCESSOR jsonProcessor) {
        if (jsonProcessor != null) {
            options.setJsonProcessor(jsonProcessor);
            reset();
        }

        return this;
    }

    public Oson asJackson() {
        return setJsonProcessor(JSON_PROCESSOR.JACKSON);
    }

    public Oson asGson() {
        return setJsonProcessor(JSON_PROCESSOR.GSON);
    }

    public Oson asOson() {
        return setJsonProcessor(JSON_PROCESSOR.OSON);
    }

    private FIELD_NAMING getFieldNaming() {
        return options.getFieldNaming();
    }

    public Oson setFieldNaming(FIELD_NAMING fieldNaming) {
        if (fieldNaming != null) {
            options.setFieldNaming(fieldNaming);
            reset();
        }

        return this;
    }

    private JSON_INCLUDE getDefaultType() {
        return options.getDefaultType();
    }

    public Oson setDefaultType(JSON_INCLUDE defaultType) {
        if (defaultType != null) {
            options.setDefaultType(defaultType);
            reset();
        }

        return this;
    }

    private String getSimpleDateFormat() {
        return options.getSimpleDateFormat();
    }

    private DateFormat getDateFormat() {
        return options.getDateFormat();
    }

    public <T> Oson setDateFormat(Class<T> type, String simpleDateFormat) {
        cMap(type).setSimpleDateFormat(simpleDateFormat);
        reset();

        return this;
    }

    public Oson setDateFormat(String simpleDateFormat) {
        if (simpleDateFormat != null) {
            options.setSimpleDateFormat(simpleDateFormat);
            reset();
        }

        return this;
    }

    public Oson setDateFormat(DateFormat dateFormat) {
        options.setDateFormat(dateFormat);
        reset();

        return this;
    }

    public Oson setDateFormat(int style) {
        options.setDateFormat(DateFormat.getDateInstance(style));
        reset();

        return this;
    }

    public <T> Oson setDateFormat(Class<T> type, int style) {
        cMap(type).setDateFormat(DateFormat.getDateInstance(style));
        reset();

        return this;
    }

    public Oson setDateFormat(int style, Locale locale) {
        options.setDateFormat(DateFormat.getDateInstance(style, locale));
        reset();

        return this;
    }

    public <T> Oson setDateFormat(Class<T> type, int style, Locale locale) {
        cMap(type).setDateFormat(DateFormat.getDateInstance(style, locale));
        reset();

        return this;
    }

    public Oson setDateFormat(int dateStyle, int timeStyle) {
        options.setDateFormat(DateFormat.getDateTimeInstance(dateStyle, timeStyle));
        reset();

        return this;
    }

    public <T> Oson setDateFormat(Class<T> type, int dateStyle, int timeStyle) {
        cMap(type).setDateFormat(DateFormat.getDateTimeInstance(dateStyle, timeStyle));
        reset();

        return this;
    }

    public Oson setDateFormat(int dateStyle, int timeStyle, Locale locale) {
        options.setDateFormat(DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale));
        reset();

        return this;
    }

    public <T> Oson setDateFormat(Class<T> type, int dateStyle, int timeStyle, Locale locale) {
        cMap(type).setDateFormat(DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale));
        reset();

        return this;
    }

    private boolean getPrettyPrinting() {
        return options.getPrettyPrinting();
    }

    public Oson pretty() {
        return pretty(true);
    }

    public Oson pretty(Boolean prettyPrinting) {
        return prettyPrinting(prettyPrinting);
    }

    public Oson prettyPrinting(Boolean prettyPrinting) {
        if (prettyPrinting != null) {
            options.prettyPrinting(prettyPrinting);
            reset();
        }

        return this;
    }

    private String getPrettySpace() {
        if (getPrettyPrinting() && getIndentation() > 0) {
            return String.valueOf(StringUtil.SPACE);
        }

        return "";
    }

    private String getPrettyIndentation(int level) {
        if (options.getPrettyPrinting() && getIndentation() > 0) {
            return StringUtil.repeatSpace(level * getIndentation());
        }

        return "";
    }

    private String getPrettyIndentationln(int level) {
        if (options.getPrettyPrinting() && getIndentation() > 0) {
            return "\n" + StringUtil.repeatSpace(level * getIndentation());
        }

        return "";
    }

    private boolean getAnnotationSupport() {
        return options.getAnnotationSupport();
    }

    public Oson setAnnotationSupport(boolean annotationSupport) {
        options.setAnnotationSupport(annotationSupport);
        reset();

        return this;
    }

    private int getPrettyIndentation() {
        if (!getPrettyPrinting()) {
            return 0;
        }
        return options.getIndentation();
    }

    private int getIndentation() {
        return options.getIndentation();
    }

    /**
     * This method is used to configure the Json output during serialization.
     * A default value of 2 is used if not specified otherwise for pretty printing.
     * @param indentation The indented spaces, with a value range of 0 to 100
     * @return Oson object, allowing this method to be chained up as a builder method
     */
    public Oson setIndentation(int indentation) {
        if (indentation != getIndentation() && indentation >= 0) {
            options.setIndentation(indentation);
            reset();
        }

        return this;
    }

    private Set<Class> getIgnoreFieldsWithAnnotations() {
        return options.getIgnoreFieldsWithAnnotations();
    }

    public Oson ignoreFieldsWithAnnotations(Set<Class> ignoreFieldsWithAnnotations) {
        options.ignoreFieldsWithAnnotations(ignoreFieldsWithAnnotations);

        return this;
    }

    public Oson ignoreFieldsWithAnnotations(Class[] ignoreFieldsWithAnnotations) {
        options.ignoreFieldsWithAnnotations(ignoreFieldsWithAnnotations);

        return this;
    }

    public Oson ignoreFieldsWithAnnotation(Class ignoreFieldsWithAnnotation) {
        options.ignoreFieldsWithAnnotation(ignoreFieldsWithAnnotation);

        return this;
    }

    private Set<Class> getIgnoreClassWithAnnotations() {
        return options.getIgnoreClassWithAnnotations();
    }

    public Oson ignoreClassWithAnnotations(Set<Class> ignoreClassWithAnnotations) {
        options.ignoreClassWithAnnotations(ignoreClassWithAnnotations);

        return this;
    }

    public Oson ignoreClassWithAnnotations(Class[] ignoreClassWithAnnotations) {
        options.ignoreClassWithAnnotations(ignoreClassWithAnnotations);

        return this;
    }

    public Oson ignoreClassWithAnnotation(Class ignoreClassWithAnnotation) {
        options.ignoreClassWithAnnotation(ignoreClassWithAnnotation);

        return this;
    }

    private Set<MODIFIER> getIncludeFieldsWithModifiers() {
        return options.getIncludeFieldsWithModifiers();
    }

    public Oson includeFieldsWithModifiers(Set<MODIFIER> includeFieldsWithModifiers) {
        options.includeFieldsWithModifiers(includeFieldsWithModifiers);

        return this;
    }

    public Oson includeFieldsWithModifiers(MODIFIER[] includeFieldsWithModifiers) {
        options.includeFieldsWithModifiers(includeFieldsWithModifiers);

        return this;
    }

    public Oson includeFieldsWithModifier(MODIFIER includeFieldsWithModifier) {
        options.includeFieldsWithModifiers(includeFieldsWithModifier);

        return this;
    }

    private InstanceCreator getTypeAdapter(Class type) {
        ClassMapper classMapper = getClassMapper(type);

        if (classMapper == null || classMapper.ignore()) {
            return null;
        }

        return classMapper.constructor;
    }

    private String java2Json(String name) {
        return options.java2Json(name);
    }

    private String json2Java(String name) {
        return options.json2Java(name);
    }

    private String java2Json(Field field) {
        return options.java2Json(field);
    }

    private String json2Java(Field field) {
        return options.json2Java(field);
    }

    private Function getSerializer(String defaultName, Class valueType, Class enclosingType) {
        return options.getSerializer(defaultName, valueType, enclosingType);
    }

    private Function getSerializer(Class valueType) {
        return options.getSerializer(valueType);
    }

    private Function getDeserializer(String defaultName, Class valueType, Class enclosingType) {
        return options.getDeserializer(defaultName, valueType, enclosingType);
    }

    private Function getDeserializer(Class valueType) {
        return options.getDeserializer(valueType);
    }

    private Object getDefaultValue(Class valueType) {
        Object value = options.getDefaultValue(valueType);

        if (value == null) {
            value = DefaultValue.getSystemDefault(valueType);
        }

        return value;
    }

    private boolean ignoreClass(Class valueType) {
        Set<MODIFIER> set = getIncludeFieldsWithModifiers();
        if (valueType.isSynthetic() && (set == null || !set.contains(MODIFIER.Synthetic))) {
            return true;
        }

        Map<Class, ClassMapper> classMappers = getClassMappers();
        if (classMappers != null) {
            ClassMapper mapper = classMappers.get(valueType);

            if (mapper != null) {
                return mapper.ignore;
            }
        }

        return false;
    }

    private boolean ignoreClass(Annotation annotation) {
        if (annotation instanceof JsonIgnoreType) {
            JsonIgnoreType jsonIgnoreType = (JsonIgnoreType) annotation;
            if (jsonIgnoreType.value()) {
                return true;
            }

        } else if (annotation instanceof org.codehaus.jackson.annotate.JsonIgnoreType) {
            org.codehaus.jackson.annotate.JsonIgnoreType jsonIgnoreType = (org.codehaus.jackson.annotate.JsonIgnoreType) annotation;
            if (jsonIgnoreType.value()) {
                return true;
            }
        }

        Set<Class> annotations = getIgnoreClassWithAnnotations();
        if (annotations == null || annotation == null) {
            return false;
        }

        for (Class ann : annotations) {
            if (annotation.getClass() == ann || ann.isAssignableFrom(annotation.getClass())) {
                return true;
            }
        }

        return false;
    }

    private boolean ignoreField(Annotation annotation, Set<Class> annotations) {
        // Set<Class> annotations = getIgnoreFieldsWithAnnotations();
        if (annotations == null || annotation == null) {
            return false;
        }

        for (Class ann : annotations) {
            if (ann != null && (annotation.getClass() == ann || ann.isAssignableFrom(annotation.getClass()))) {
                return true;
            }
        }

        return ignoreField(annotation.getClass(), annotations);
    }

    private boolean ignoreField(Class annotationClass, Set<Class> annotations) {
        if (annotations == null || annotationClass == null) {
            return false;
        }

        for (Class ann : annotations) {
            if (ann != null && (annotationClass == ann || ann.isAssignableFrom(annotationClass))) {
                return true;
            }
        }

        return false;
    }

    private boolean ignoreModifiers(int modifiers, Set<MODIFIER> includeFieldsWithModifiers) {
        //Set<MODIFIER> includeFieldsWithModifiers = getIncludeFieldsWithModifiers();
        if (includeFieldsWithModifiers == null || includeFieldsWithModifiers.size() == 0) {
            // by default, transient and volatile are ignored
            // unless you specify otherwise, by using MODIFIER.Transient enum, or all
            if (Modifier.isTransient(modifiers)) {
                return true;
            }
            if (Modifier.isVolatile(modifiers)) {
                return true;
            }

            return false;
        }

        if (includeFieldsWithModifiers.contains(MODIFIER.All)) {
            return false;
        }

        for (MODIFIER modifier : includeFieldsWithModifiers) {
            switch (modifier) {
            case Abstract:
                if (Modifier.isAbstract(modifiers)) {
                    return false;
                }
                break;
            case Final:
                if (Modifier.isFinal(modifiers)) {
                    return false;
                }
                break;
            case Interface:
                if (Modifier.isInterface(modifiers)) {
                    return false;
                }
                break;
            case Native:
                if (Modifier.isNative(modifiers)) {
                    return false;
                }
                break;
            case Private:
                if (Modifier.isPrivate(modifiers)) {
                    return false;
                }
                break;
            case Protected:
                if (Modifier.isProtected(modifiers)) {
                    return false;
                }
                break;
            case Public:
                if (Modifier.isPublic(modifiers)) {
                    return false;
                }
                break;
            case Package:
                if (ObjectUtil.isPackage(modifiers)) {
                    return false;
                }
                break;
            case Static:
                if (Modifier.isStatic(modifiers)) {
                    return false;
                }
                break;
            case Strict:
                if (Modifier.isStrict(modifiers)) {
                    return false;
                }
                break;
            case Synchronized:
                if (Modifier.isSynchronized(modifiers)) {
                    return false;
                }
                break;
            case Transient:
                if (Modifier.isTransient(modifiers)) {
                    return false;
                }
                break;
            case Volatile:
                if (Modifier.isVolatile(modifiers)) {
                    return false;
                }
                break;
            }
        }

        return true;
    }

    private Boolean getOrderArrayAndList() {
        return options.getOrderArrayAndList();
    }

    public Oson orderArrayAndList(Boolean orderArrayAndList) {
        if (orderArrayAndList != null) {
            options.setOrderArrayAndList(orderArrayAndList);
        }

        return this;
    }

    private Boolean getOrderByKeyAndProperties() {
        return options.getOrderByKeyAndProperties();
    }

    public Oson orderByKeyAndProperties(Boolean orderByKeyAndProperties) {
        if (orderByKeyAndProperties != null) {
            options.setOrderByKeyAndProperties(orderByKeyAndProperties);
            reset();
        }

        return this;
    }

    public Oson sort(boolean order) {
        orderArrayAndList(order);
        return orderByKeyAndProperties(order);
    }

    public Oson sort() {
        return sort(true);
    }

    private Boolean getIncludeClassTypeInJson() {
        return options.getIncludeClassTypeInJson();
    }

    public Oson includeClassTypeInJson(Boolean includeClassTypeInJson) {
        if (includeClassTypeInJson != null) {
            options.setIncludeClassTypeInJson(includeClassTypeInJson);
            reset();
        }

        return this;
    }

    private Boolean getPrintErrorUseOsonInFailure() {
        return options.getPrintErrorUseOsonInFailure();
    }

    public Oson setPrintErrorUseOsonInFailure(Boolean printErrorUseOsonInFailure) {
        if (printErrorUseOsonInFailure != null) {
            options.setPrintErrorUseOsonInFailure(printErrorUseOsonInFailure);
            reset();
        }

        return this;
    }

    private String getJsonClassType() {
        return options.getJsonClassType();
    }

    public Oson setJsonClassType(String jsonClassType) {
        if (jsonClassType != null) {
            options.setJsonClassType(jsonClassType);
            reset();
        }

        return this;
    }

    private Double getVersion() {
        return options.getVersion();
    }

    public Oson setVersion(Double version) {
        options.setVersion(version);
        reset();

        return this;
    }

    private Map<Class, ClassMapper> getClassMappers() {
        return options.getClassMappers();
    }

    private ClassMapper getClassMapper(Class valueType) {
        if (valueType == null) {
            return null;
        }

        // change primitive type to object type
        valueType = ObjectUtil.getObjectType(valueType);

        Map<Class, ClassMapper> mappers = getClassMappers();

        if (mappers == null) {
            return null;
        }

        ClassMapper classMapper = mappers.get(valueType);

        if (classMapper != null) {
            return classMapper;
        }

        for (Class cls : mappers.keySet()) {
            if (cls.isAssignableFrom(valueType)) {
                return mappers.get(cls);
            }
        }

        return null;
    }

    /*
     * if a specific attribute is null, set it to the global setting
     */
    private ClassMapper globalize(ClassMapper mapper) {
        // a little bit convenient
        // && Date.class.isAssignableFrom(valueType)
        if (mapper.getDateFormat() == null) {
            mapper.setDateFormat(getDateFormat());
        }

        if (mapper.includeClassTypeInJson == null) {
            mapper.includeClassTypeInJson = getIncludeClassTypeInJson();
        }

        if (mapper.orderByKeyAndProperties == null) {
            mapper.orderByKeyAndProperties = getOrderByKeyAndProperties();
        }

        if (mapper.orderArrayAndList == null) {
            mapper.orderArrayAndList = getOrderArrayAndList();
        }

        if (mapper.useAttribute == null) {
            mapper.useAttribute = isUseAttribute();
        }

        if (mapper.useField == null) {
            mapper.useField = isUseField();
        }

        if (mapper.ignoreFieldsWithAnnotations == null) {
            mapper.ignoreFieldsWithAnnotations = getIgnoreFieldsWithAnnotations();
        }

        //      if (mapper.since == null) {
        //         mapper.since = getVersion();
        //      }

        if (mapper.includeFieldsWithModifiers == null) {
            mapper.includeFieldsWithModifiers = getIncludeFieldsWithModifiers();
        }

        if (mapper.defaultType == null) {
            mapper.defaultType = getDefaultType();
        }

        if (mapper.enumType == null) {
            mapper.enumType = getEnumType();
        }

        if (mapper.date2Long == null) {
            mapper.date2Long = getDate2Long();
        }

        if (mapper.length == null) {
            mapper.length = getLength();
        }

        if (mapper.precision == null) {
            mapper.precision = getPrecision();
        }

        if (mapper.scale == null) {
            mapper.scale = getScale();
        }

        if (mapper.min == null) {
            mapper.min = getMin();
        }

        if (mapper.max == null) {
            mapper.max = getMax();
        }

        if (mapper.isToStringAsSerializer() == null) {
            mapper.setToStringAsSerializer(isToStringAsSerializer());
        }

        if (mapper.getEscapeHtml() == null) {
            mapper.setEscapeHtml(isEscapeHtml());
        }

        return mapper;
    }

    private ClassMapper getGlobalizedClassMapper(Class valueType) {
        ClassMapper mapper = getClassMapper(valueType);

        if (mapper == null) {
            mapper = new ClassMapper();
        }

        return globalize(mapper);
    }

    public Oson setClassMappers(Map<Class, ClassMapper> classMappers) {
        options.setClassMappers(classMappers);
        reset();

        return this;
    }

    public Oson setClassMappers(ClassMapper[] classMappers) {
        options.setClassMappers(classMappers);
        reset();

        return this;
    }

    public Oson setClassMappers(List<ClassMapper> classMappers) {
        options.setClassMappers(classMappers);
        reset();

        return this;
    }

    public Oson setClassMappers(ClassMapper classMapper) {
        options.setClassMappers(classMapper);
        reset();

        return this;
    }

    public Oson setClassMappers(Class type, ClassMapper classMapper) {
        options.setClassMappers(type, classMapper);
        reset();

        return this;
    }

    private Set<FieldMapper> getFieldMappers() {
        return options.getFieldMappers();
    }

    private FieldMapper classifyFieldMapper(FieldMapper fieldMapper, ClassMapper classMapper) {
        if (classMapper == null) {
            return fieldMapper;
        }

        // classify it now
        if (fieldMapper.useAttribute == null) {
            fieldMapper.useAttribute = classMapper.useAttribute;
        }

        if (fieldMapper.useField == null) {
            fieldMapper.useField = classMapper.useField;
        }

        if (fieldMapper.getDateFormat() == null) {
            fieldMapper.setDateFormat(classMapper.getDateFormat());
        }

        if (fieldMapper.defaultType == null) {
            fieldMapper.defaultType = classMapper.defaultType;
        }

        if (fieldMapper.enumType == null) {
            fieldMapper.enumType = classMapper.enumType;
        }

        if (fieldMapper.date2Long == null) {
            fieldMapper.date2Long = classMapper.date2Long;
        }

        if (fieldMapper.precision == null) {
            fieldMapper.precision = classMapper.precision;
        }

        if (fieldMapper.scale == null) {
            fieldMapper.scale = classMapper.scale;
        }

        if (fieldMapper.min == null) {
            fieldMapper.min = classMapper.min;
        }

        if (fieldMapper.max == null) {
            fieldMapper.max = classMapper.max;
        }

        if (fieldMapper.length == null) {
            fieldMapper.length = classMapper.length;
        }

        if (fieldMapper.defaultValue == null) {
            fieldMapper.defaultValue = classMapper.defaultValue;
        }

        if (fieldMapper.ignore == null) {
            fieldMapper.ignore = classMapper.ignore;
        }

        return fieldMapper;
    }

    public Oson setFieldMappers(Set<FieldMapper> fieldMappers) {
        options.setFieldMappers(fieldMappers);
        reset();

        return this;
    }

    public Oson setFieldMappers(Collection<FieldMapper> fieldMappers) {
        options.setFieldMappers(fieldMappers);
        reset();

        return this;
    }

    public Oson setFieldMappers(FieldMapper[] fieldMappers) {
        options.setFieldMappers(fieldMappers);
        reset();

        return this;
    }

    public Oson setFieldMappers(FieldMapper fieldMapper) {
        options.setFieldMappers(fieldMapper);
        reset();

        return this;
    }

    public <T> Oson setFieldMappers(Class<T> type, BOOLEAN deserializing, String... javaJsons) {
        if (javaJsons.length < 2) {
            return this;
        }

        for (int i = 0; i + 1 < javaJsons.length; i++) {
            FieldMapper fieldMapper = new FieldMapper(javaJsons[i], javaJsons[++i], type);
            fieldMapper.setDeserializing(deserializing);
            options.setFieldMappers(fieldMapper);
        }

        reset();

        return this;
    }

    private FieldMapper getFieldMapper(String java, String json, Class type) {
        FieldMapper fieldMapper = options.getFieldMapper(java, json, type);
        if (fieldMapper == null) {
            fieldMapper = options.getFieldMapper(java, json, null);
        }

        return fieldMapper;
    }

    private Set<FieldMapper> getFieldMappers(Class type) {
        return options.getFieldMappers(type);
    }

    private Boolean isUseField() {
        return options.isUseField();
    }

    public Oson useField(Boolean useField) {
        options.setUseField(useField);
        reset();

        return this;
    }

    private Boolean isUseAttribute() {
        return options.isUseAttribute();
    }

    public Oson useAttribute(Boolean useAttribute) {
        options.setUseAttribute(useAttribute);
        reset();

        return this;
    }

    private int getLevel() {
        return options.getLevel();
    }

    /**
     * This method is used to control the depth of attribute processing.
     * A Java class can hold attributes of Classes which can hold instance variables
     * of other classes. This level setting will limit the depth of processing during
     * serialization and de-serialization. A MAX_LEVEL constant is set to 100, so no more
     * than 100 levels of depth is allowed. This can certain prevents endless processing
     * loop in case other measures fail.
     * @param level The depth of Json-Java processing, with a value range of 0 to 100
     * @return Oson object, allowing this method to be chained up as a builder method
     */
    public Oson setLevel(int level) {
        options.setLevel(level);

        return this;
    }

    private EnumType getEnumType() {
        return options.getEnumType();
    }

    public Oson setEnumType(EnumType enumType) {
        options.setEnumType(enumType);

        return this;
    }

    private Boolean getDate2Long() {
        return options.getDate2Long();
    }

    public Oson setDate2Long(Boolean date2Long) {
        options.setDate2Long(date2Long);

        return this;
    }

    private RoundingMode getRoundingMode() {
        return options.getRoundingMode();
    }

    public Oson setRoundingMode(RoundingMode roundingMode) {
        options.setRoundingMode(roundingMode);

        return this;
    }

    private Integer getLength() {
        return options.getLength();
    }

    public Oson setLength(Integer length) {
        options.setLength(length);

        return this;
    }

    private Integer getPrecision() {
        return options.getPrecision();
    }

    public Oson setPrecision(Integer precision) {
        options.setPrecision(precision);

        return this;
    }

    private Integer getScale() {
        return options.getScale();
    }

    public Oson setScale(Integer scale) {
        options.setScale(scale);

        return this;
    }

    public <T> Oson setScale(Class<T> type, Integer scale) {
        cMap(type).setScale(scale);

        return this;
    }

    public boolean isAppendingFloatingZero() {
        return options.isAppendingFloatingZero();
    }

    public Oson setAppendingFloatingZero(boolean appendingFloatingZero) {
        options.setAppendingFloatingZero(appendingFloatingZero);

        return this;
    }

    private Long getMin() {
        return options.getMin();
    }

    public Oson setMin(Long min) {
        options.setMin(min);

        return this;
    }

    private Long getMax() {
        return options.getMax();
    }

    public Oson setMax(Long max) {
        options.setMax(max);

        return this;
    }

    private boolean getSetGetOnly() {
        return options.getSetGetOnly();
    }

    public Oson setGetOnly(boolean setGetOnly) {
        options.setSetGetOnly(setGetOnly);

        return this;
    }

    public Oson setGetOnly() {
        return setGetOnly(true);
    }

    private boolean isInheritMapping() {
        return options.isInheritMapping();
    }

    public Oson setInheritMapping(boolean inheritMapping) {
        options.setInheritMapping(inheritMapping);

        return this;
    }

    private boolean isUseGsonExpose() {
        return options.isUseGsonExpose();
    }

    public Oson useGsonExpose(boolean useGsonExpose) {
        options.setUseGsonExpose(useGsonExpose);

        return this;
    }

    public Oson setCommentPatterns() {
        return setCommentPatterns(Options.defaultPatterns);
    }

    public Oson setCommentPatterns(String[] commentPatterns) {
        options.setCommentPatterns(commentPatterns);

        return this;
    }

    private Pattern[] getPatterns() {
        return options.getPatterns();
    }

    public Oson excludeFieldsWithModifiers(int... modifiers) {
        options.excludeFieldsWithModifiers(modifiers);

        return this;
    }

    private boolean isMap2ListStyle() {
        return options.isMap2ListStyle();
    }

    public Oson setMap2ListStyle(boolean map2ListStyle) {
        options.setMap2ListStyle(map2ListStyle);

        return this;
    }

    private boolean isValueOnly() {
        return options.isValueOnly();
    }

    public Oson setValueOnly(boolean valueOnly) {
        options.setValueOnly(valueOnly);

        return this;
    }

    /////////////////////////////////////////////////////////////////////////////////
    // start to set up class mapper

    private <T> ClassMapper cMap(Class<T> type) {
        return this.options.getClassMappers(type);
    }

    public <T> Oson setConstructor(Class<T> type, InstanceCreator<T> constructor) {
        cMap(type).setConstructor(constructor);

        return this;
    }

    public <T> Oson setDefaultValue(Class<T> type, T defaultValue) {
        cMap(type).setDefaultValue(defaultValue);

        return this;
    }

    public <T> Oson setIgnore(Class<T> type, boolean ignore) {
        cMap(type).setIgnore(ignore);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Function serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Function deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Integer2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2IntegerFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Long2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2LongFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Double2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2DoubleFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Short2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2ShortFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Float2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2FloatFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, BigDecimal2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2BigDecimalFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, BigInteger2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2BigIntegerFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Character2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2CharacterFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Byte2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2ByteFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Boolean2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2BooleanFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Date2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2DateFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Date2LongFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Long2DateFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Enum2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2EnumFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Collection2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2CollectionFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Map2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2MapFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, Array2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2ArrayFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, AtomicInteger2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2AtomicIntegerFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, AtomicLong2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2AtomicLongFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, DataMapper2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2DataMapperFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, FieldData2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2FieldDataFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson setSerializer(Class<T> type, String2JsonFunction serializer) {
        cMap(type).setSerializer(serializer);

        return this;
    }

    public <T> Oson setDeserializer(Class<T> type, Json2StringFunction deserializer) {
        cMap(type).setDeserializer(deserializer);

        return this;
    }

    public <T> Oson ser(Class<T> type, Function serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Function deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Integer2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2IntegerFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Long2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2LongFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Double2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2DoubleFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Short2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2ShortFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Float2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2FloatFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, BigDecimal2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2BigDecimalFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, BigInteger2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2BigIntegerFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Character2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2CharacterFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Byte2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2ByteFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Boolean2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2BooleanFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Date2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2DateFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Date2LongFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Long2DateFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Enum2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2EnumFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Collection2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2CollectionFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Map2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2MapFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, Array2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2ArrayFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, AtomicInteger2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2AtomicIntegerFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, AtomicLong2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2AtomicLongFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, DataMapper2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2DataMapperFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, FieldData2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2FieldDataFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson ser(Class<T> type, String2JsonFunction serializer) {
        return setSerializer(type, serializer);
    }

    public <T> Oson des(Class<T> type, Json2StringFunction deserializer) {
        return setDeserializer(type, deserializer);
    }

    public <T> Oson useField(Class<T> type, Boolean useField) {
        cMap(type).setUseField(useField);

        return this;
    }

    public <T> Oson useAttribute(Class<T> type, Boolean useAttribute) {
        cMap(type).setUseAttribute(useAttribute);

        return this;
    }

    public <T> Oson setIncludeFieldsWithModifiers(Class<T> type, Set<MODIFIER> includeFieldsWithModifiers) {
        cMap(type).setIncludeFieldsWithModifiers(includeFieldsWithModifiers);

        return this;
    }

    public <T> Oson setSimpleDateFormat(Class<T> type, String simpleDateFormat) {
        cMap(type).setSimpleDateFormat(simpleDateFormat);

        return this;
    }

    public <T> Oson setDateFormat(Class<T> type, DateFormat dateFormat) {
        cMap(type).setDateFormat(dateFormat);

        return this;
    }

    public <T> Oson setOrderByKeyAndProperties(Class<T> type, Boolean orderByKeyAndProperties) {
        cMap(type).setOrderByKeyAndProperties(orderByKeyAndProperties);

        return this;
    }

    public <T> Oson setOrderArrayAndList(Class<T> type, Boolean orderArrayAndList) {
        cMap(type).setOrderArrayAndList(orderArrayAndList);

        return this;
    }

    public <T> Oson setPropertyOrders(Class<T> type, String[] propertyOrders) {
        cMap(type).setPropertyOrders(propertyOrders);

        return this;
    }

    public <T> Oson setIncludeClassTypeInJson(Class<T> type, Boolean includeClassTypeInJson) {
        cMap(type).setIncludeClassTypeInJson(includeClassTypeInJson);

        return this;
    }

    public <T> Oson setVersion(Class<T> type, Double version) {
        cMap(type).setSince(version);

        return this;
    }

    public <T> Oson setIgnoreFieldsWithAnnotations(Class<T> type, Set<Class> ignoreFieldsWithAnnotations) {
        cMap(type).setIgnoreFieldsWithAnnotations(ignoreFieldsWithAnnotations);

        return this;
    }

    public <T> Oson setJsonIgnoreProperties(Class<T> type, Set<String> jsonIgnoreProperties) {
        cMap(type).setJsonIgnoreProperties(jsonIgnoreProperties);

        return this;
    }

    public <T> Oson setDefaultType(Class<T> type, JSON_INCLUDE defaultType) {
        cMap(type).setDefaultType(defaultType);

        return this;
    }

    public <T> Oson setMin(Class<T> type, Long min) {
        cMap(type).setMin(min);

        return this;
    }

    public <T> Oson setMax(Class<T> type, Long max) {
        cMap(type).setMax(max);

        return this;
    }

    public <T> Oson setEnumType(Class<T> type, EnumType enumType) {
        cMap(type).setEnumType(enumType);

        return this;
    }

    public <T> Oson setDate2Long(Class<T> type, Boolean date2Long) {
        cMap(type).setDate2Long(date2Long);

        return this;
    }

    public <T> Oson setLength(Class<T> type, Integer length) {
        cMap(type).setLength(length);

        return this;
    }

    public <T> Oson setPrecision(Class<T> type, Integer precision) {
        cMap(type).setPrecision(precision);

        return this;
    }

    // end of setting up class mapper

    private boolean isToStringAsSerializer() {
        return options.isToStringAsSerializer();
    }

    public Oson setToStringAsSerializer(boolean toStringAsSerializer) {
        options.setToStringAsSerializer(toStringAsSerializer);

        return this;
    }

    public <T> Oson setToStringAsSerializer(Class<T> type, boolean toStringAsSerializer) {
        cMap(type).setToStringAsSerializer(toStringAsSerializer);

        return this;
    }

    public <T> Oson setEscapeHtml(Class<T> type, boolean escapeHtml) {
        cMap(type).setEscapeHtml(escapeHtml);

        return this;
    }

    public boolean isEscapeHtml() {
        return options.isEscapeHtml();
    }

    public Oson setEscapeHtml(boolean escapeHtml) {
        options.setEscapeHtml(escapeHtml);

        return this;
    }

    public <T> Oson setJsonValueFieldName(Class<T> type, String jsonValueFieldName) {
        cMap(type).setJsonValueFieldName(jsonValueFieldName);

        return this;
    }

    private void reset() {
        jackson = null;
        gson = null;
    }

    public Oson clear() {
        options = new Options();
        reset();

        return this;
    }

    public Oson clearAll() {
        clear();

        cachedFields.clear();
        cachedMethods.clear();
        cachedComponentTypes.clear();

        return this;
    }

    public ObjectMapper getJackson() {
        if (jackson == null) {
            jackson = new ObjectMapper();
            prefixProcessing();

            // do not fail for funny reason
            jackson.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
            jackson.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

            switch (getDefaultType()) {
            case ALWAYS:
                jackson.setSerializationInclusion(Include.ALWAYS);
                break;
            case NON_NULL:
                jackson.setSerializationInclusion(Include.NON_NULL);
                break;
            case NON_EMPTY:
                jackson.setSerializationInclusion(Include.NON_EMPTY);
                break;
            case DEFAULT:
                jackson.setSerializationInclusion(Include.USE_DEFAULTS);
                break;
            default:
                jackson.setSerializationInclusion(Include.NON_NULL);
                break;
            }

            if (getPrettyPrinting() && getIndentation() > 0) {
                jackson.enable(SerializationFeature.INDENT_OUTPUT);
                jackson.configure(SerializationFeature.INDENT_OUTPUT, true);
            }

            if (getOrderByKeyAndProperties()) {
                jackson.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
            }

            Set<FieldMapper> mappers = getFieldMappers();
            if (mappers != null) {
                //defines how names of JSON properties ("external names") are derived from names of POJO methods and fields ("internal names"),
                // in cases where they are not auto-detected and no explicit annotations exist for naming
                // config - Configuration in used: either SerializationConfig or DeserializationConfig,
                // depending on whether method is called during serialization or deserialization
                jackson.setPropertyNamingStrategy(new PropertyNamingStrategy() {
                    @Override
                    public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
                        String name = defaultName;
                        if (name == null) {
                            name = field.getName();
                        }
                        name = name.trim();
                        if (name.isEmpty()) {
                            return super.nameForField(config, field, defaultName);
                        }

                        String lname = name.toLowerCase();

                        if (config instanceof SerializationConfig || !(config instanceof DeserializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String java = fieldMapper.java;

                                if (java != null && lname.equals(java.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = field.getFullName(); // ca.oson.json.Artist#age;
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.json;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.json;
                                    }
                                }
                            }

                        }

                        if (config instanceof DeserializationConfig || !(config instanceof SerializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String json = fieldMapper.json;

                                if (json != null && lname.equals(json.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = field.getFullName(); // ca.oson.json.Artist#age;
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.java;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.java;
                                    }
                                }
                            }

                        }

                        return super.nameForField(config, field, defaultName);
                    }

                    @Override
                    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method,
                            String defaultName) {
                        String name = defaultName;
                        if (name == null) {
                            name = method.getName().substring(3);
                        }
                        name = name.trim();
                        if (name.isEmpty()) {
                            return super.nameForGetterMethod(config, method, defaultName);
                        }

                        String lname = name.toLowerCase();

                        if (config instanceof SerializationConfig || !(config instanceof DeserializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String java = fieldMapper.java;

                                if (java != null && lname.equals(java.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = method.getFullName(); // java.util.Date#getTime(0 params)
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.json;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.json;
                                    }
                                }
                            }

                        }

                        if (config instanceof DeserializationConfig || !(config instanceof SerializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String json = fieldMapper.json;

                                if (json != null && lname.equals(json.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = method.getFullName(); // java.util.Date#getTime(0 params)
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.java;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.java;
                                    }
                                }
                            }

                        }

                        return super.nameForGetterMethod(config, method, defaultName);
                    }

                    /*
                     * (non-Javadoc)
                     * @see com.fasterxml.jackson.databind.PropertyNamingStrategy#nameForSetterMethod(com.fasterxml.jackson.databind.cfg.MapperConfig, com.fasterxml.jackson.databind.introspect.AnnotatedMethod, java.lang.String)
                     *
                     * Method called to find external name (name used in JSON) for given logical POJO property, as defined by given setter method; typically called when building a deserializer (but not necessarily only then).
                     */
                    @Override
                    public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method,
                            String defaultName) {
                        String name = defaultName;
                        if (name == null) {
                            name = method.getName().substring(3);
                        }
                        name = name.trim();
                        if (name.isEmpty()) {
                            return super.nameForSetterMethod(config, method, defaultName);
                        }

                        String lname = name.toLowerCase();

                        if (config instanceof SerializationConfig || !(config instanceof DeserializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String java = fieldMapper.java;

                                if (java != null && lname.equals(java.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = method.getFullName(); // java.util.Date#getTime(0 params)
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.json;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.json;
                                    }
                                }
                            }

                        }

                        if (config instanceof DeserializationConfig || !(config instanceof SerializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String json = fieldMapper.json;

                                if (json != null && lname.equals(json.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = method.getFullName(); // java.util.Date#getTime(0 params)
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.java;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.java;
                                    }
                                }
                            }

                        }

                        return super.nameForSetterMethod(config, method, defaultName);
                    }

                    /*
                     * (non-Javadoc)
                     * @see com.fasterxml.jackson.databind.PropertyNamingStrategy#nameForConstructorParameter(com.fasterxml.jackson.databind.cfg.MapperConfig, com.fasterxml.jackson.databind.introspect.AnnotatedParameter, java.lang.String)
                     *
                     * find external name (name used in JSON) for given logical POJO property, as defined by given setter method; typically called when building a deserializer (but not necessarily only then).
                     */
                    @Override
                    public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
                            String defaultName) {
                        String name = defaultName;
                        if (name == null) {
                            name = ctorParam.getName();
                        }
                        name = name.trim();
                        if (name.isEmpty()) {
                            return super.nameForConstructorParameter(config, ctorParam, defaultName);
                        }

                        String lname = name.toLowerCase();

                        if (config instanceof SerializationConfig || !(config instanceof DeserializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String java = fieldMapper.java;

                                if (java != null && lname.equals(java.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = ctorParam.getName(); // java.util.Date#
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.json;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        String json = fieldMapper.json;

                                        if (json == null) {
                                            // do nothing?

                                        } else {
                                            return json;
                                        }
                                    }
                                }
                            }

                        }

                        if (config instanceof DeserializationConfig || !(config instanceof SerializationConfig)) {
                            for (FieldMapper fieldMapper : mappers) {
                                String json = fieldMapper.json;

                                if (json != null && lname.equals(json.toLowerCase())) {
                                    Class<?> type = fieldMapper.getType();
                                    String fullName = ctorParam.getName(); // java.util.Date#
                                    int index = fullName.indexOf('#');
                                    if (index > -1) {
                                        fullName = fullName.substring(0, index);
                                    }

                                    if (type != null) {
                                        try {
                                            // mapper.getType().getTypeName() is the same as mapper.getType().getName()
                                            if (type.getTypeName().equals(fullName)) {
                                                return fieldMapper.java;
                                            }

                                        } catch (SecurityException e) {
                                            // e.printStackTrace();
                                        }

                                    } else { // just by name
                                        return fieldMapper.java;
                                    }
                                }
                            }

                        }

                        return super.nameForConstructorParameter(config, ctorParam, defaultName);
                    }

                });
            }

            jackson.setDateFormat(getDateFormat());
        }

        return jackson;
    }

    private void prefixProcessing() {
        if (getJsonProcessor() != JSON_PROCESSOR.JACKSON) {
            return;
        }

        Set<FieldMapper> mappers = getFieldMappers();
        if (mappers == null) {
            return;
        }

        for (FieldMapper fieldMapper : mappers) {
            if (fieldMapper.isProcessed()) {
                continue;
            }

            String json = fieldMapper.json;
            String java = fieldMapper.java;

            if (fieldMapper.getType() == null || json == null || java == null) {
                fieldMapper.setProcessed(true);
                continue;
            }

            String className = fieldMapper.getType().getName();
            if (fieldMapper.ignore != null && fieldMapper.ignore) {

                try {
                    if (java != null) {
                        ObjectUtil.addAnnotationToField(className, java,
                                "com.fasterxml.jackson.annotation.JsonIgnore");

                    } else if (json != null) {
                        ObjectUtil.addAnnotationToField(className, json,
                                "com.fasterxml.jackson.annotation.JsonIgnore");
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
                fieldMapper.setProcessed(true);
            }

            if (json == null) {
                // serialize
                // String methodName = "get" + StringUtil.capitalize(java);
                try {
                    ObjectUtil.addAnnotationToField(className, java, "com.fasterxml.jackson.annotation.JsonIgnore");
                    fieldMapper.setProcessed(true);

                } catch (Exception e) {
                    e.printStackTrace();
                    if (!fieldMapper.isProcessed()) {
                        // should ignore it, have to use mixin abstract class
                        try {
                            ObjectUtil.addAnnotationToField(className, java,
                                    "com.fasterxml.jackson.annotation.JsonIgnore", mixin);

                            jackson.addMixInAnnotations(fieldMapper.getClass(), Class.forName(className + mixin));

                            fieldMapper.setProcessed(true);

                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }

                    }
                }

            } else if (java == null) {
                // deserialize
                //String methodName = "set" + StringUtil.capitalize(json);
                try {
                    ObjectUtil.addAnnotationToField(className, json, "com.fasterxml.jackson.annotation.JsonIgnore");
                    fieldMapper.setProcessed(true);
                } catch (Exception e) {
                    e.printStackTrace();
                    if (!fieldMapper.isProcessed()) {
                        // should ignore it, have to use mixin abstract class
                        try {
                            ObjectUtil.addAnnotationToField(className, json,
                                    "com.fasterxml.jackson.annotation.JsonIgnore", mixin);

                            jackson.addMixInAnnotations(fieldMapper.getClass(), Class.forName(className + mixin));

                            fieldMapper.setProcessed(true);

                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }

                    }
                }
            }

        }
    }

    public Gson getGson() {
        if (gson == null) {
            GsonBuilder gsonBuilder = new GsonBuilder();

            switch (getDefaultType()) {
            case ALWAYS:
                gsonBuilder.serializeNulls();
                break;
            case NON_NULL:
                break;
            case NON_EMPTY:
                break;
            case DEFAULT:
                gsonBuilder.serializeNulls();
                break;
            default:
                gsonBuilder.serializeNulls();
                break;
            }

            switch (getFieldNaming()) {
            case FIELD: // original field name: someField_name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY);
                break;
            case LOWER: // somefield_name -> some_field_name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
                break;
            case UPPER: //SOMEFIELD_NAME -> SomeFieldName
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
                break;
            case CAMELCASE: // someFieldName
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY);
                break;
            case UPPER_CAMELCASE: // SomeFieldName
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
                break;
            case UNDERSCORE_CAMELCASE: // some_Field_Name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
                break;
            case UNDERSCORE_UPPER_CAMELCASE: // Some_Field_Name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
                break;
            case UNDERSCORE_LOWER: // some_field_name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
                break;
            case UNDERSCORE_UPPER: // SOME_FIELD_NAME
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
                break;
            case SPACE_CAMELCASE: // some Field Name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
                break;
            case SPACE_UPPER_CAMELCASE: // Some Field Name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
                break;
            case SPACE_LOWER: // some field name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
                break;
            case SPACE_UPPER: // SOME FIELD NAME
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
                break;
            case DASH_CAMELCASE: // some-Field-Name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
                break;
            case DASH_UPPER_CAMELCASE: // Some-Field-Name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
                break;
            case DASH_LOWER: // some-field-name
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
                break;
            case DASH_UPPER: // SOME-FIELD-NAME
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
                break;
            default:
                gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY);
                break;
            }

            if (getPrettyPrinting() && getIndentation() > 0) {
                gsonBuilder.setPrettyPrinting();
            }

            gsonBuilder.setDateFormat(options.getSimpleDateFormat());

            Set<FieldMapper> mappers = getFieldMappers();
            Map<Class, ClassMapper> classMappers = getClassMappers();
            List<ExclusionStrategy> strategies = new ArrayList<>();
            if (mappers != null) {
                gsonBuilder.setFieldNamingStrategy(new FieldNamingStrategy() {
                    @Override
                    public String translateName(Field f) {
                        String fieldName = f.getName();
                        String serializedName = java2Json(f);
                        if (!fieldName.equalsIgnoreCase(serializedName)) {
                            // if null returned, this field is ignored
                            return serializedName;
                        }
                        return json2Java(f);
                    }
                });

                for (FieldMapper mapper : mappers) {
                    if (mapper.java == null || mapper.json == null || mapper.ignore) {
                        strategies.add(new ExclusionStrategy() {

                            @Override
                            public boolean shouldSkipField(FieldAttributes f) {
                                String name = f.getName();
                                Class cls = f.getClass();

                                if (mapper.java == null) {
                                    if (mapper.json.equals(name)) {
                                        if (mapper.getType() == null || cls.equals(mapper.getType())) {
                                            return true;
                                        }
                                    }

                                } else if (mapper.json == null || mapper.ignore) {
                                    if (mapper.java.equals(name)) {
                                        if (mapper.getType() == null || cls.equals(mapper.getType())) {
                                            return true;
                                        }
                                    }

                                }

                                return false;
                            }

                            @Override
                            public boolean shouldSkipClass(Class<?> clazz) {
                                return false;
                            }
                        });
                    }
                }
            }

            if (classMappers != null) {
                for (Entry<Class, ClassMapper> entry : classMappers.entrySet()) {
                    ClassMapper mapper = entry.getValue();
                    if (mapper.getType() == null) {
                        mapper.setType(entry.getKey());
                    }
                    if (mapper.ignore != null && mapper.ignore) {
                        strategies.add(new ExclusionStrategy() {

                            @Override
                            public boolean shouldSkipField(FieldAttributes f) {
                                return false;
                            }

                            @Override
                            public boolean shouldSkipClass(Class<?> clazz) {
                                if (clazz.equals(mapper.getType())) {
                                    return true;
                                }
                                return false;
                            }
                        });
                    }
                }

                for (Entry<Class, ClassMapper> entry : classMappers.entrySet()) {
                    ClassMapper mapper = entry.getValue();
                    if (!mapper.ignore && mapper.constructor != null) {
                        gsonBuilder.registerTypeAdapter(entry.getKey(), mapper.constructor);
                    }
                }
            }

            int size = strategies.size();
            if (size > 0) {
                gsonBuilder.setExclusionStrategies(strategies.toArray(new ExclusionStrategy[size]));
            }

            Double version = getVersion();
            if (version != null) {
                gsonBuilder.setVersion(version);
            }

            if (isUseGsonExpose()) {
                gsonBuilder.excludeFieldsWithoutExposeAnnotation();
            }

            if (!DefaultValue.isDefault(getPatterns())) {
                gsonBuilder.setLenient();
            }

            gson = gsonBuilder.create();
        }

        return gson;
    }

    ////////////////////////////////////////////////////////////////////////////////
    // END OF CONFIGURATION
    ////////////////////////////////////////////////////////////////////////////////

    private static Object getMapValue(Map<String, Object> map, String name) {
        return getMapValue(map, name, null);
    }

    private static Object getMapValue(Map<String, Object> map, String name, Set<String> set) {
        if (map == null) {
            return null;
        }

        for (FIELD_NAMING format : FIELD_NAMING.values()) {
            String key = StringUtil.formatName(name, format);
            if (map.containsKey(key)) {
                if (set != null) {
                    set.remove(key);
                }
                return map.get(key);
            }
        }

        return null;
    }

    private Function getDeserializer(FieldData objectDTO) {
        Function function = objectDTO.getDeserializer();
        if (function != null) {
            return function;
        }

        return getDeserializer(objectDTO.getDefaultName(), objectDTO.returnType, objectDTO.getEnclosingType());
    }

    private Function getSerializer(FieldData objectDTO) {
        Function function = objectDTO.getSerializer();
        if (function != null) {
            return function;
        }

        return getSerializer(objectDTO.getDefaultName(), objectDTO.returnType, objectDTO.getEnclosingType());
    }

    private <E, R> Double json2Double(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Double valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2DoubleFunction) {
                            return ((Json2DoubleFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof Double) {
                                valueToReturn = (Double) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = Double.parseDouble((String) returnedValue);

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = ((Integer) returnedValue).doubleValue();
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = ((Long) returnedValue).doubleValue();
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = ((Byte) returnedValue).doubleValue();
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = ((Short) returnedValue).doubleValue();
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = ((Float) returnedValue).doubleValue();
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = ((BigInteger) returnedValue).doubleValue();
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = ((BigDecimal) returnedValue).doubleValue();
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = ((AtomicInteger) returnedValue).doubleValue();
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = ((AtomicLong) returnedValue).doubleValue();
                            } else {
                                valueToReturn = ((Number) returnedValue).doubleValue();
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = (double) (((Character) returnedValue).charValue());

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = 1d;
                            else
                                valueToReturn = 0d;

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = ((Integer) ((Enum) returnedValue).ordinal()).doubleValue();

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = (double) ((Date) returnedValue).getTime();

                        } else {
                            valueToReturn = Double.parseDouble(returnedValue.toString());
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = Double.parseDouble(valueToProcess);
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min.doubleValue() > valueToReturn) {
                        return min.doubleValue();
                    }

                    if (max != null && max.doubleValue() < valueToReturn) {
                        valueToReturn = max.doubleValue();
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2DoubleDefault(objectDTO);
    }

    private <E, R> String double2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && returnType != null && (returnType == double.class || returnType == Double.class)) {
            Double valueToProcess = null;
            String valueToReturn = null;

            if (returnType == double.class) {
                valueToProcess = Double.valueOf((Double) value);
            } else {
                valueToProcess = (Double) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Double2JsonFunction) {
                                return ((Double2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Double) {
                                    valueToProcess = (Double) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min.doubleValue() > valueToProcess) {
                            valueToProcess = min.doubleValue();
                        }

                        if (max != null && max.doubleValue() < valueToProcess) {
                            valueToProcess = max.doubleValue();
                        }

                        Integer precision = objectDTO.getPrecision();
                        Integer scale = objectDTO.getScale();

                        String result = null;

                        if (precision != null) {
                            if (scale != null) {
                                valueToProcess = (double) NumberUtil.setPrecision(valueToProcess, precision,
                                        getRoundingMode());
                                BigDecimal b = new BigDecimal(valueToProcess);

                                b = b.setScale(scale, getRoundingMode());

                                result = NumberUtil.toPlainString(b);

                            } else {
                                result = NumberUtil.precision2Json(valueToProcess, precision, getRoundingMode());
                            }

                        } else if (scale != null) {
                            BigDecimal b = new BigDecimal(valueToProcess);

                            b = b.setScale(scale, getRoundingMode());
                            result = NumberUtil.toPlainString(b);

                        } else {
                            result = NumberUtil.toPlainString(valueToProcess);
                        }

                        return NumberUtil.appendingFloatingZero(result, isAppendingFloatingZero());
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return double2JsonDefault(objectDTO);
    }

    private String double2JsonDefault(FieldData objectDTO) {
        Double valueToReturn = json2DoubleDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> Double json2DoubleDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == double.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Double defaultValue = (Double) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min.doubleValue() > defaultValue) {
                    return min.doubleValue();
                }

                return defaultValue;
            }

            if (min != null && min.doubleValue() > DefaultValue.ddouble) {
                return min.doubleValue();
            }

            return DefaultValue.ddouble;
        }

        return null;
    }

    private <E, R> Float json2Float(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Float valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2FloatFunction) {
                            return ((Json2FloatFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof Float) {
                                valueToReturn = (Float) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = Float.parseFloat((String) returnedValue);

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = ((Integer) returnedValue).floatValue();
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = ((Long) returnedValue).floatValue();
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = ((Short) returnedValue).floatValue();
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = ((Double) returnedValue).floatValue();
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = ((Byte) returnedValue).floatValue();
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = ((BigInteger) returnedValue).floatValue();
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = ((BigDecimal) returnedValue).floatValue();
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = ((AtomicInteger) returnedValue).floatValue();
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = ((AtomicLong) returnedValue).floatValue();
                            } else {
                                valueToReturn = ((Number) returnedValue).floatValue();
                            }

                        } else if (returnedValue instanceof Character) {
                            char c = (Character) returnedValue;
                            valueToReturn = (float) c;

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = 1f;
                            else
                                valueToReturn = 0f;

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = ((Integer) ((Enum) returnedValue).ordinal()).floatValue();

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = (float) ((Date) returnedValue).getTime();

                        } else {
                            valueToReturn = Float.parseFloat(returnedValue.toString());
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    double doubleValue = Double.parseDouble(valueToProcess);

                    if (doubleValue > Float.MAX_VALUE) {
                        valueToReturn = Float.MAX_VALUE;
                    } else {
                        valueToReturn = (float) doubleValue;
                    }

                    // valueToReturn = Float.parseFloat(valueToProcess);
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min.floatValue() > valueToReturn) {
                        return min.floatValue();
                    }

                    if (max != null && max.floatValue() < valueToReturn) {
                        valueToReturn = max.floatValue();
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2FloatDefault(objectDTO);
    }

    private <E, R> String float2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (value != null && returnType != null && (returnType == float.class || returnType == Float.class)) {
            Float valueToProcess = null;
            String valueToReturn = null;

            if (returnType == float.class) {
                valueToProcess = Float.valueOf((Float) value);
            } else {
                valueToProcess = (Float) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Float2JsonFunction) {
                                return ((Float2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Float) {
                                    valueToProcess = (Float) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min.floatValue() > valueToProcess) {
                            valueToProcess = min.floatValue();
                        }

                        if (max != null && max.floatValue() < valueToProcess) {
                            valueToProcess = max.floatValue();
                        }

                        Integer precision = objectDTO.getPrecision();
                        Integer scale = objectDTO.getScale();

                        String result = null;

                        if (precision != null) {
                            if (scale != null) {
                                valueToProcess = (float) NumberUtil.setPrecision(valueToProcess, precision,
                                        getRoundingMode());
                                BigDecimal b = new BigDecimal(valueToProcess);

                                b = b.setScale(scale, getRoundingMode());
                                result = NumberUtil.toPlainString(b);

                            } else {
                                result = NumberUtil.precision2Json(valueToProcess, precision, getRoundingMode());
                            }

                        } else if (scale != null) {
                            BigDecimal b = new BigDecimal(valueToProcess);

                            b = b.setScale(scale, getRoundingMode());
                            result = NumberUtil.toPlainString(b);
                        } else {
                            result = NumberUtil.toPlainString(valueToProcess);
                        }

                        return NumberUtil.appendingFloatingZero(result, isAppendingFloatingZero());
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return float2JsonDefault(objectDTO);
    }

    private String float2JsonDefault(FieldData objectDTO) {
        Float valueToReturn = json2FloatDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> Float json2FloatDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == float.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Float defaultValue = (Float) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min.floatValue() > defaultValue) {
                    return min.floatValue();
                }

                return defaultValue;
            }

            if (min != null && min.floatValue() > DefaultValue.dfloat) {
                return min.floatValue();
            }

            return DefaultValue.dfloat;
        }

        return null;
    }

    private Date date2Date(Date currentDate, Class returnType) {
        if (currentDate == null) {
            return currentDate;
        }

        Class cls = currentDate.getClass();

        if (returnType.isAssignableFrom(cls)) {
            return currentDate;
        }

        if (Date.class != returnType) {
            return date2Date(currentDate.getTime(), returnType);
        } else {
            return currentDate;
        }
    }

    private Date date2Date(long longdatetime, Class returnType) {
        if (java.sql.Date.class == returnType) {
            return new java.sql.Date(longdatetime);

        } else if (java.sql.Time.class == returnType) {
            //currentDate.getHours(), currentDate.getMinutes(), currentDate.getSeconds()
            return new java.sql.Time(longdatetime);

        } else if (java.sql.Timestamp.class == returnType) {
            return new java.sql.Timestamp(longdatetime);
        } else {
            return new Date(longdatetime);
        }
    }

    private <E, R> Date json2Date(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2DateFunction) {
                            return ((Json2DateFunction) function).apply(valueToProcess);

                        } else if (function instanceof Long2DateFunction) {
                            long longtoprocess = Long
                                    .parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess));
                            return ((Long2DateFunction) function).apply(longtoprocess);

                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {

                            return date2Date((Date) returnedValue, returnType);

                        } else {
                            valueToProcess = String.valueOf(returnedValue);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

                if (valueToProcess != null) {
                    valueToProcess = StringUtil.unquote(valueToProcess, isEscapeHtml());
                    if (StringUtil.isNumeric(valueToProcess)) {
                        try {
                            long longdatetime = Long
                                    .parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess));
                            return date2Date(longdatetime, returnType);
                        } catch (Exception e) {
                        }
                    }

                    try {
                        DateFormat format = objectDTO.getDateFormat();
                        return date2Date(format.parse(valueToProcess), returnType);
                    } catch (Exception e) {
                        return date2Date(new Date(valueToProcess), returnType);
                    }

                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return date2Date(json2DateDefault(objectDTO), returnType);
    }

    private <E, R> String date2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            DateFormat format = objectDTO.getDateFormat();
            Date valueToProcess = null;
            String valueToReturn = null;

            if (value instanceof Date) {
                valueToProcess = (Date) value;
            } else {
                try {
                    valueToProcess = format.parse(value.toString().trim());
                } catch (Exception ex) {
                }
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Date2JsonFunction) {
                                return ((Date2JsonFunction) function).apply(valueToProcess);

                            } else if (function instanceof Date2LongFunction) {
                                Long longtoprocess = ((Date2LongFunction) function).apply(valueToProcess);
                                return longtoprocess.toString();

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Date) {
                                    valueToProcess = (Date) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Boolean date2Long = objectDTO.getDate2Long();
                        if (date2Long != null && date2Long) {
                            long longtoprocess = valueToProcess.getTime();
                            return NumberUtil.toPlainString(longtoprocess);
                        } else {
                            return format.format(valueToProcess);
                        }
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return date2JsonDefault(objectDTO);
    }

    private String date2JsonDefault(FieldData objectDTO) {
        Date valueToReturn = json2DateDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        objectDTO.valueToProcess = valueToReturn;
        return object2String(objectDTO);
    }

    private <E> Date json2DateDefault(FieldData objectDTO) {
        if (getDefaultType() == JSON_INCLUDE.DEFAULT || objectDTO.required()) {
            Date defaultValue = (Date) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                return defaultValue;
            }

            return DefaultValue.getDate();
        }

        return null;
    }

    private <E, R> BigDecimal json2BigDecimal(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            BigDecimal valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2BigDecimalFunction) {
                            return ((Json2BigDecimalFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof BigDecimal) {
                                valueToReturn = (BigDecimal) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = new BigDecimal((String) returnedValue);

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = new BigDecimal((Integer) returnedValue);
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = new BigDecimal((Long) returnedValue);
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = new BigDecimal((Short) returnedValue);
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = new BigDecimal((Double) returnedValue);
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = new BigDecimal((Float) returnedValue);
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = new BigDecimal((BigInteger) returnedValue);
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = new BigDecimal((Byte) returnedValue);
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = new BigDecimal(((AtomicInteger) returnedValue).intValue());
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = new BigDecimal(((AtomicLong) returnedValue).longValue());
                            } else {
                                valueToReturn = new BigDecimal(((Number) returnedValue).doubleValue());
                            }

                        } else if (returnedValue instanceof Character) {
                            char c = (Character) returnedValue;
                            valueToReturn = new BigDecimal(Character.getNumericValue((Character) returnedValue));

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = new BigDecimal(1);
                            else
                                valueToReturn = new BigDecimal(0);

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = new BigDecimal((Integer) ((Enum) returnedValue).ordinal());

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = new BigDecimal(((Date) returnedValue).getTime());

                        } else {
                            valueToReturn = new BigDecimal((returnedValue.toString()));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = new BigDecimal(valueToProcess);
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && valueToReturn.compareTo(new BigDecimal(min)) < 0) {
                        return new BigDecimal(min);
                    }

                    if (max != null && valueToReturn.compareTo(new BigDecimal(max)) > 0) {
                        valueToReturn = new BigDecimal(max);
                    }

                    Integer precision = objectDTO.getPrecision();
                    if (precision != null && precision < valueToReturn.precision()) {
                        valueToReturn = (BigDecimal) NumberUtil.setPrecision(valueToReturn, precision,
                                getRoundingMode());
                    }

                    Integer scale = objectDTO.getScale();
                    if (scale != null) {
                        valueToReturn = valueToReturn.setScale(scale, getRoundingMode());
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2BigDecimalDefault(objectDTO);
    }

    private <E, R> String bigDecimal2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            BigDecimal valueToProcess = null;
            String valueToReturn = null;

            if (value instanceof BigDecimal) {
                valueToProcess = (BigDecimal) value;
            } else {
                try {
                    valueToProcess = new BigDecimal(value.toString().trim());
                } catch (Exception ex) {
                }
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof BigDecimal2JsonFunction) {
                                return ((BigDecimal2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof BigDecimal) {
                                    valueToProcess = (BigDecimal) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && valueToProcess.compareTo(new BigDecimal(min)) < 0) {
                            valueToProcess = new BigDecimal(min);
                        }

                        if (max != null && valueToProcess.compareTo(new BigDecimal(max)) > 0) {
                            valueToProcess = new BigDecimal(max);
                        }

                        Integer precision = objectDTO.getPrecision();
                        Integer scale = objectDTO.getScale();

                        if (precision != null && precision < valueToProcess.precision()) {
                            valueToProcess = (BigDecimal) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());

                            if (scale != null) {
                                valueToProcess = valueToProcess.setScale(scale, getRoundingMode());
                            }

                        } else if (scale != null) {
                            valueToProcess = valueToProcess.setScale(scale, getRoundingMode());
                        }

                        return NumberUtil.appendingFloatingZero(NumberUtil.toPlainString(valueToProcess),
                                this.isAppendingFloatingZero());
                    }

                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }

        return bigDecimal2JsonDefault(objectDTO);
    }

    private String bigDecimal2JsonDefault(FieldData objectDTO) {
        BigDecimal valueToReturn = json2BigDecimalDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> BigDecimal json2BigDecimalDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            BigDecimal defaultValue = (BigDecimal) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && defaultValue.compareTo(new BigDecimal(min)) < 0) {
                    return new BigDecimal(min);
                }

                if (max != null && defaultValue.compareTo(new BigDecimal(max)) > 0) {
                    return new BigDecimal(max);
                }

                return defaultValue;
            }

            if (min != null && DefaultValue.bigDecimal.compareTo(new BigDecimal(min)) < 0) {
                return new BigDecimal(min);
            }

            return DefaultValue.bigDecimal;
        }

        return null;
    }

    private <E, R> BigInteger json2BigInteger(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            BigInteger valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2BigIntegerFunction) {
                            return ((Json2BigIntegerFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof BigInteger) {
                                valueToReturn = (BigInteger) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = new BigInteger((String) returnedValue);

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = BigInteger.valueOf((Integer) returnedValue);
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = BigInteger.valueOf((Long) returnedValue);
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = BigInteger.valueOf((Short) returnedValue);
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = BigInteger.valueOf(((Double) returnedValue).longValue());
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = BigInteger.valueOf(((Float) returnedValue).intValue());
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = BigInteger.valueOf(((BigDecimal) returnedValue).longValue());
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = BigInteger.valueOf((Byte) returnedValue);
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = BigInteger.valueOf(((AtomicInteger) returnedValue).intValue());
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = BigInteger.valueOf(((AtomicLong) returnedValue).longValue());
                            } else {
                                valueToReturn = BigInteger.valueOf(((Number) returnedValue).longValue());
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = BigInteger.valueOf(((Character) returnedValue));

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = BigInteger.valueOf(1);
                            else
                                valueToReturn = BigInteger.valueOf(0);

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = BigInteger.valueOf(((Enum) returnedValue).ordinal());

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = BigInteger.valueOf(((Date) returnedValue).getTime());

                        } else {
                            valueToReturn = new BigInteger((returnedValue.toString()));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = new BigInteger(valueToProcess);
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min > valueToReturn.longValue()) {
                        return BigInteger.valueOf(min.intValue());
                    }

                    if (max != null && valueToReturn.compareTo(BigInteger.valueOf(max)) > 0) {
                        valueToReturn = BigInteger.valueOf(max);
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2BigIntegerDefault(objectDTO);
    }

    private <E, R> String bigInteger2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            BigInteger valueToProcess = null;
            String valueToReturn = null;

            if (value instanceof BigInteger) {
                valueToProcess = (BigInteger) value;
            } else {
                try {
                    valueToProcess = new BigInteger(value.toString().trim());
                } catch (Exception ex) {
                }
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof BigInteger2JsonFunction) {
                                return ((BigInteger2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof BigInteger) {
                                    valueToProcess = (BigInteger) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min > valueToProcess.longValue()) {
                            valueToProcess = BigInteger.valueOf(min);
                        }

                        if (max != null && valueToProcess.compareTo(BigInteger.valueOf(max)) > 0) {
                            valueToProcess = BigInteger.valueOf(max);
                        }

                        Integer precision = objectDTO.getPrecision();
                        if (precision != null) {
                            valueToProcess = (BigInteger) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return bigInteger2JsonDefault(objectDTO);
    }

    private String bigInteger2JsonDefault(FieldData objectDTO) {
        BigInteger valueToReturn = json2BigIntegerDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> BigInteger json2BigIntegerDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            BigInteger defaultValue = (BigInteger) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min > defaultValue.longValue()) {
                    return BigInteger.valueOf(min);
                }

                if (max != null && defaultValue.compareTo(BigInteger.valueOf(max)) > 0) {
                    return BigInteger.valueOf(max);
                }

                return defaultValue;
            }

            if (min != null && DefaultValue.bigInteger.compareTo(BigInteger.valueOf(min)) < 0) {
                return BigInteger.valueOf(min);
            }

            return DefaultValue.bigInteger;
        }

        return null;
    }

    private <E, R> AtomicInteger json2AtomicInteger(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            AtomicInteger valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;
                        // suppose to return AtomicInteger, but in case not, try to process
                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2AtomicIntegerFunction) {
                            return ((Json2AtomicIntegerFunction) function).apply(valueToProcess);

                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = (AtomicInteger) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = new AtomicInteger(
                                        Integer.parseInt(NumberUtil.removeTrailingDecimalZeros(returnedValue)));

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = new AtomicInteger((Integer) returnedValue);
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = new AtomicInteger(((Long) returnedValue).intValue());
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = new AtomicInteger((Short) returnedValue);
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = new AtomicInteger(((Double) returnedValue).intValue());
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = new AtomicInteger(((Float) returnedValue).intValue());
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = new AtomicInteger(((BigDecimal) returnedValue).intValue());
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = new AtomicInteger((Byte) returnedValue);
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = new AtomicInteger(((BigInteger) returnedValue).intValue());
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = new AtomicInteger(((AtomicLong) returnedValue).intValue());
                            } else {
                                valueToReturn = new AtomicInteger(((Number) returnedValue).intValue());
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = new AtomicInteger(((Character) returnedValue));

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = new AtomicInteger(1);
                            else
                                valueToReturn = new AtomicInteger(0);

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = new AtomicInteger(((Enum) returnedValue).ordinal());

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = new AtomicInteger((int) ((Date) returnedValue).getTime());

                        } else {
                            valueToReturn = new AtomicInteger(
                                    Integer.parseInt(NumberUtil.removeTrailingDecimalZeros(returnedValue)));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = new AtomicInteger(
                            Integer.parseInt(NumberUtil.removeTrailingDecimalZeros(valueToProcess)));
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min > valueToReturn.longValue()) {
                        return new AtomicInteger(min.intValue());
                    }

                    if (max != null && valueToReturn.longValue() > max) {
                        valueToReturn = new AtomicInteger(max.intValue());
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2AtomicIntegerDefault(objectDTO);
    }

    private <E, R> String atomicInteger2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            AtomicInteger valueToProcess = null;
            String valueToReturn = null;

            if (value instanceof AtomicInteger) {
                valueToProcess = (AtomicInteger) value;
            } else {
                try {
                    valueToProcess = new AtomicInteger(
                            Integer.parseInt(NumberUtil.removeTrailingDecimalZeros(value)));
                } catch (Exception ex) {
                }
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof AtomicInteger2JsonFunction) {
                                return ((AtomicInteger2JsonFunction) function).apply(valueToProcess);

                            } else {
                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof AtomicInteger) {
                                    valueToProcess = (AtomicInteger) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min > valueToProcess.longValue()) {
                            valueToProcess = new AtomicInteger(min.intValue());
                        }

                        if (max != null && max < valueToProcess.longValue()) {
                            valueToProcess = new AtomicInteger(max.intValue());
                        }

                        Integer precision = objectDTO.getPrecision();
                        if (precision != null) {
                            valueToProcess = (AtomicInteger) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return atomicInteger2JsonDefault(objectDTO);
    }

    private String atomicInteger2JsonDefault(FieldData objectDTO) {
        AtomicInteger valueToReturn = json2AtomicIntegerDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> AtomicInteger json2AtomicIntegerDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            AtomicInteger defaultValue = (AtomicInteger) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min > defaultValue.longValue()) {
                    return new AtomicInteger(min.intValue());
                }

                if (max != null && max < defaultValue.longValue()) {
                    return new AtomicInteger(max.intValue());
                }

                return defaultValue;
            }

            if (min != null && min > DefaultValue.atomicInteger.longValue()) {
                return new AtomicInteger(min.intValue());
            }

            return DefaultValue.atomicInteger;
        }

        return null;
    }

    private <E, R> AtomicLong json2AtomicLong(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            AtomicLong valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2AtomicLongFunction) {
                            return ((Json2AtomicLongFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof AtomicLong) {
                                valueToReturn = (AtomicLong) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = new AtomicLong(
                                        Long.parseLong(NumberUtil.removeTrailingDecimalZeros(returnedValue)));

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = new AtomicLong((Integer) returnedValue);
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = new AtomicLong(((Long) returnedValue).longValue());
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = new AtomicLong((Short) returnedValue);
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = new AtomicLong(((Double) returnedValue).longValue());
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = new AtomicLong(((Float) returnedValue).intValue());
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = new AtomicLong(((BigDecimal) returnedValue).longValue());
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = new AtomicLong((Byte) returnedValue);
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = new AtomicLong(((BigInteger) returnedValue).longValue());
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = new AtomicLong(((AtomicInteger) returnedValue).intValue());
                            } else {
                                valueToReturn = new AtomicLong(((Number) returnedValue).longValue());
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = new AtomicLong(((Character) returnedValue));

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = new AtomicLong(1);
                            else
                                valueToReturn = new AtomicLong(0);

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = new AtomicLong(((Enum) returnedValue).ordinal());

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = new AtomicLong(((Date) returnedValue).getTime());

                        } else {
                            valueToReturn = new AtomicLong(
                                    Long.parseLong(NumberUtil.removeTrailingDecimalZeros(returnedValue)));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = new AtomicLong(
                            Long.parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess)));
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min > valueToReturn.longValue()) {
                        return new AtomicLong(min);
                    }

                    if (max != null && valueToReturn.longValue() > max) {
                        valueToReturn = new AtomicLong(max);
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2AtomicLongDefault(objectDTO);
    }

    private <E, R> String atomicLong2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            AtomicLong valueToProcess = null;
            String valueToReturn = null;

            if (value instanceof AtomicLong) {
                valueToProcess = (AtomicLong) value;
            } else {
                try {
                    valueToProcess = new AtomicLong(Long.parseLong(NumberUtil.removeTrailingDecimalZeros(value)));
                } catch (Exception ex) {
                }
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof AtomicLong2JsonFunction) {
                                return ((AtomicLong2JsonFunction) function).apply(valueToProcess);

                            } else {
                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof AtomicLong) {
                                    valueToProcess = (AtomicLong) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min > valueToProcess.longValue()) {
                            valueToProcess = new AtomicLong(min);
                        }

                        if (max != null && max < valueToProcess.longValue()) {
                            valueToProcess = new AtomicLong(max);
                        }

                        Integer precision = objectDTO.getPrecision();
                        if (precision != null) {
                            valueToProcess = (AtomicLong) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return atomicLong2JsonDefault(objectDTO);
    }

    private String atomicLong2JsonDefault(FieldData objectDTO) {
        AtomicLong valueToReturn = json2AtomicLongDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> AtomicLong json2AtomicLongDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            AtomicLong defaultValue = (AtomicLong) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min > defaultValue.longValue()) {
                    return new AtomicLong(min);
                }

                if (max != null && max < defaultValue.longValue()) {
                    return new AtomicLong(max);
                }

                return defaultValue;
            }

            if (min != null && min > DefaultValue.atomicLong.longValue()) {
                return new AtomicLong(min);
            }

            return DefaultValue.atomicLong;
        }

        return null;
    }

    private <E, R> String long2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (returnType != null && value != null && (returnType == long.class || returnType == Long.class)) {
            Long valueToProcess = null;

            if (returnType == long.class) {
                valueToProcess = Long.valueOf((Long) value);
            } else {
                valueToProcess = (Long) value;
            }

            if (valueToProcess != null) {
                try {

                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        if (function instanceof DataMapper2JsonFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            return ((DataMapper2JsonFunction) function).apply(classData);

                        } else if (function instanceof Long2JsonFunction) {
                            return ((Long2JsonFunction) function).apply(valueToProcess);

                        } else {

                            Object returnedValue = function.apply(valueToProcess);

                            if (returnedValue instanceof Optional) {
                                returnedValue = ObjectUtil.unwrap(returnedValue);
                            }

                            if (returnedValue == null) {
                                return null;

                            } else if (returnedValue instanceof Long) {
                                valueToProcess = (Long) returnedValue;

                            } else {
                                objectDTO.valueToProcess = returnedValue;
                                return object2String(objectDTO);
                            }

                        }

                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min > valueToProcess.longValue()) {
                            valueToProcess = min.longValue();
                        }

                        if (max != null && valueToProcess.compareTo(Long.valueOf(max)) > 0) {
                            valueToProcess = Long.valueOf(max);
                        }

                        Integer precision = objectDTO.getPrecision();
                        if (precision != null) {
                            valueToProcess = (Long) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    // ex.printStackTrace();
                }
            }
        }

        return long2JsonDefault(objectDTO);
    }

    private <E, R> Long json2Long(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        Long valueToReturn = null;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2LongFunction) {
                            return ((Json2LongFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                            //  || returnedValue.getClass().isPrimitive()
                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())) {

                            if (returnedValue instanceof Long) {
                                valueToReturn = (Long) returnedValue;

                            } else if (returnedValue instanceof String) {
                                valueToReturn = Long
                                        .parseLong(NumberUtil.removeTrailingDecimalZeros(returnedValue));
                                //  byte, double, float, int, long, and short.
                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = ((Integer) returnedValue).longValue();
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = ((Double) returnedValue).longValue();
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = ((Float) returnedValue).longValue();
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = ((Byte) returnedValue).longValue();
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = ((Short) returnedValue).longValue();
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = ((BigInteger) returnedValue).longValue();
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = ((BigDecimal) returnedValue).longValue();
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = ((AtomicInteger) returnedValue).longValue();
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = ((AtomicLong) returnedValue).longValue();
                            } else {
                                valueToReturn = ((Number) returnedValue).longValue();
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = Long.valueOf((Character.getNumericValue((Character) returnedValue)));

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = Long.valueOf(((Enum) returnedValue).ordinal());

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = 1l;
                            else
                                valueToReturn = 0l;

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = ((Date) returnedValue).getTime();

                        } else {
                            valueToReturn = Long.parseLong(NumberUtil.removeTrailingDecimalZeros(returnedValue));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = Long.parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess));
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();
                    if (min != null && min > valueToReturn.longValue()) {
                        return min.longValue();
                    }

                    if (max != null && valueToReturn.compareTo(Long.valueOf(max)) > 0) {
                        valueToReturn = Long.valueOf(max);
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2LongDefault(objectDTO);
    }

    private String long2JsonDefault(FieldData objectDTO) {
        Long value = json2LongDefault(objectDTO);

        if (value == null) {
            return null;
        }

        return value.toString();
    }

    private <E, R> Long json2LongDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();

        if (returnType == long.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Long defaultValue = (Long) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min > defaultValue.longValue()) {
                    return min.longValue();
                }

                return defaultValue;
            }

            if (min != null && min > DefaultValue.dlong) {
                return min.longValue();
            }

            return DefaultValue.dlong;
        }

        return null;
    }

    private <E, R> R json2Integer(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Integer valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2IntegerFunction) {
                            return (R) ((Json2IntegerFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof Integer) {
                                valueToReturn = (Integer) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = Integer
                                        .parseInt(NumberUtil.removeTrailingDecimalZeros(returnedValue));
                                //  byte, double, float, int, long, and short.
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = ((Long) returnedValue).intValue();
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = ((Byte) returnedValue).intValue();
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = ((Short) returnedValue).intValue();
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = ((Float) returnedValue).intValue();
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = ((Double) returnedValue).intValue();
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = ((BigInteger) returnedValue).intValue();
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = ((BigDecimal) returnedValue).intValue();
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = ((AtomicInteger) returnedValue).intValue();
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = ((AtomicLong) returnedValue).intValue();
                            } else {
                                valueToReturn = ((Number) returnedValue).intValue();
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = Character.getNumericValue((Character) returnedValue);

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = 1;
                            else
                                valueToReturn = 0;

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = ((Enum) returnedValue).ordinal();

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = (int) ((Date) returnedValue).getTime();

                        } else {
                            valueToReturn = Integer.parseInt(NumberUtil.removeTrailingDecimalZeros(returnedValue));
                        }

                        return (R) valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = Integer.parseInt(NumberUtil.removeTrailingDecimalZeros(valueToProcess));
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();
                    if (min != null && min > valueToReturn.longValue()) {
                        return (R) (Integer) min.intValue();
                    }

                    if (max != null && valueToReturn.longValue() > max) {
                        valueToReturn = Integer.valueOf(max.intValue());
                    }

                    return (R) valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2IntegerDefault(objectDTO);
    }

    private <E> String integer2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (value != null && returnType != null && (returnType == int.class || returnType == Integer.class)) {
            Integer valueToProcess = null;
            String valueToReturn = null;

            if (returnType == int.class) {
                valueToProcess = Integer.valueOf((int) value);
            } else {
                valueToProcess = (Integer) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Integer2JsonFunction) {
                                return ((Integer2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = function.apply(valueToProcess);

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return integer2JsonDefault(objectDTO);

                                } else if (returnedValue instanceof Integer) {
                                    valueToProcess = (Integer) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min > valueToProcess.longValue()) {
                            valueToProcess = min.intValue();
                        }

                        if (max != null && valueToProcess > max.longValue()) {
                            valueToProcess = max.intValue();
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return integer2JsonDefault(objectDTO);
    }

    private String integer2JsonDefault(FieldData objectDTO) {
        Integer valueToReturn = json2IntegerDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E, R> R json2IntegerDefault(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;
        Class<R> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        if (returnType == int.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Integer defaultValue = (Integer) objectDTO.getDefaultValue();
            Long min = objectDTO.getMin();
            Long max = objectDTO.getMax();
            if (defaultValue != null) {
                if (min != null && min > defaultValue.longValue()) {
                    return (R) (Integer) min.intValue();
                }

                return (R) defaultValue;
            }

            if (min != null && min > DefaultValue.integer) {
                return (R) (Integer) min.intValue();
            }

            return (R) DefaultValue.integer;
        }

        return null;
    }

    private <E> Byte json2Byte(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Byte valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2ByteFunction) {
                            return ((Json2ByteFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof Byte) {
                                valueToReturn = (Byte) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = Byte
                                        .parseByte(NumberUtil.removeTrailingDecimalZeros(returnedValue));

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = ((Integer) returnedValue).byteValue();
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = ((Long) returnedValue).byteValue();
                            } else if (returnedValue instanceof Short) {
                                valueToReturn = ((Short) returnedValue).byteValue();
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = ((Double) returnedValue).byteValue();
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = ((Float) returnedValue).byteValue();
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = ((BigInteger) returnedValue).byteValue();
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = ((BigDecimal) returnedValue).byteValue();
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = ((AtomicInteger) returnedValue).byteValue();
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = ((AtomicLong) returnedValue).byteValue();
                            } else {
                                valueToReturn = ((Number) returnedValue).byteValue();
                            }

                        } else if (returnedValue instanceof Character) {
                            char c = (Character) returnedValue;
                            valueToReturn = (byte) c;

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = 1;
                            else
                                valueToReturn = 0;

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = ((Integer) ((Enum) returnedValue).ordinal()).byteValue();

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = (byte) ((Date) returnedValue).getTime();

                        } else {
                            valueToReturn = Byte.parseByte(NumberUtil.removeTrailingDecimalZeros(returnedValue));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    long longValue = Long.parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess));

                    if (longValue > Byte.MAX_VALUE) {
                        valueToReturn = Byte.MAX_VALUE;
                    } else {
                        valueToReturn = (byte) longValue;
                    }

                    // valueToReturn = Byte.parseByte(valueToProcess);
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min.byteValue() > valueToReturn) {
                        return min.byteValue();
                    }

                    if (max != null && max.byteValue() < valueToReturn) {
                        valueToReturn = max.byteValue();
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2ByteDefault(objectDTO);
    }

    private <E> String byte2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (returnType != null && value != null && (returnType == byte.class || returnType == Byte.class)) {
            Byte valueToProcess = null;
            String valueToReturn = null;

            if (returnType == byte.class) {
                valueToProcess = Byte.valueOf((byte) value);
            } else {
                valueToProcess = (Byte) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Byte2JsonFunction) {
                                return ((Byte2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Byte) {
                                    valueToProcess = (Byte) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min.byteValue() > valueToProcess) {
                            valueToProcess = min.byteValue();
                        }

                        if (max != null && max.byteValue() < valueToProcess) {
                            valueToProcess = max.byteValue();
                        }

                        Integer precision = objectDTO.getPrecision();
                        if (precision != null) {
                            valueToProcess = (Byte) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return byte2JsonDefault(objectDTO);
    }

    private String byte2JsonDefault(FieldData objectDTO) {
        Byte valueToReturn = json2ByteDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E> Byte json2ByteDefault(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == byte.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Byte defaultValue = (Byte) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min.byteValue() > defaultValue) {
                    return min.byteValue();
                }

                return defaultValue;
            }

            if (min != null && min.byteValue() > DefaultValue.dbyte) {
                return min.byteValue();
            }

            return DefaultValue.dbyte;
        }

        return null;
    }

    private Character long2Character(long longvalue, Long min, Long max) {
        Character valueToReturn;

        if (min == null && longvalue < Character.MIN_CODE_POINT) {
            valueToReturn = null;
        } else if (max == null && longvalue > Character.MAX_CODE_POINT) {
            valueToReturn = null;
        } else if (min != null && min > longvalue) {
            valueToReturn = (char) min.intValue();

        } else if (max != null && max < longvalue) {
            valueToReturn = (char) max.intValue();
        } else {
            valueToReturn = (char) longvalue;
        }

        return valueToReturn;

    }

    private <E> Character json2Character(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Character valueToReturn = null;
            Long min = objectDTO.getMin();
            Long max = objectDTO.getMax();

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2CharacterFunction) {
                            return ((Json2CharacterFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (returnedValue instanceof Character) {
                            return (Character) returnedValue;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {
                            long longvalue = ((Number) returnedValue).longValue();

                            valueToReturn = long2Character(longvalue, min, max);

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = (char) 1;
                            else
                                valueToReturn = (char) 0;

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            int intvalue = ((Enum) returnedValue).ordinal();

                            valueToReturn = long2Character(intvalue, min, max);

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            long longvalue = ((Date) returnedValue).getTime();

                            valueToReturn = long2Character(longvalue, min, max);

                        } else {
                            long longvalue = Long.parseLong(NumberUtil.removeTrailingDecimalZeros(returnedValue));

                            valueToReturn = long2Character(longvalue, min, max);
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    if (valueToProcess.length() == 1) {
                        return valueToProcess.charAt(0);

                    } else {
                        long longvalue = Long.parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess));

                        valueToReturn = long2Character(longvalue, min, max);
                    }
                }

                if (valueToReturn != null) {
                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }
        }

        return json2CharacterDefault(objectDTO);
    }

    private <E> String character2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (returnType != null && value != null && (returnType == char.class || returnType == Character.class)) {
            Character valueToProcess = (Character) value;
            String valueToReturn = null;

            // convert from char to Character
            if (returnType == char.class) {
                valueToProcess = Character.valueOf((char) value); // new Character(value)
            } else {
                valueToProcess = (Character) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Character2JsonFunction) {
                                return ((Character2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Character
                                        || returnedValue.getClass() == char.class) {
                                    valueToProcess = (Character) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        return Character.toString(valueToProcess); // String.valueOf(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return character2JsonDefault(objectDTO);
    }

    private String character2JsonDefault(FieldData objectDTO) {
        Character valueToReturn = json2CharacterDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E> Character json2CharacterDefault(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == char.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Character defaultValue = (Character) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min > (int) defaultValue) {
                    return (char) min.intValue();
                }

                return defaultValue;
            }

            if (min != null && min > (int) DefaultValue.character) {
                return (char) min.intValue();
            }

            return DefaultValue.character;
        }

        return null;
    }

    private <E> Short json2Short(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Short valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2ShortFunction) {
                            return ((Json2ShortFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Number.class.isAssignableFrom(returnedValue.getClass())
                                || returnedValue.getClass().isPrimitive()) {

                            if (returnedValue instanceof Short) {
                                valueToReturn = (Short) returnedValue;
                            } else if (returnedValue instanceof String) {
                                valueToReturn = Short
                                        .parseShort(NumberUtil.removeTrailingDecimalZeros(returnedValue));

                            } else if (returnedValue instanceof Integer) {
                                valueToReturn = ((Integer) returnedValue).shortValue();
                            } else if (returnedValue instanceof Long) {
                                valueToReturn = ((Long) returnedValue).shortValue();
                            } else if (returnedValue instanceof Byte) {
                                valueToReturn = ((Byte) returnedValue).shortValue();
                            } else if (returnedValue instanceof Double) {
                                valueToReturn = ((Double) returnedValue).shortValue();
                            } else if (returnedValue instanceof Float) {
                                valueToReturn = ((Float) returnedValue).shortValue();
                            } else if (returnedValue instanceof BigInteger) {
                                valueToReturn = ((BigInteger) returnedValue).shortValue();
                            } else if (returnedValue instanceof BigDecimal) {
                                valueToReturn = ((BigDecimal) returnedValue).shortValue();
                            } else if (returnedValue instanceof AtomicInteger) {
                                valueToReturn = ((AtomicInteger) returnedValue).shortValue();
                            } else if (returnedValue instanceof AtomicLong) {
                                valueToReturn = ((AtomicLong) returnedValue).shortValue();
                            } else {
                                valueToReturn = ((Number) returnedValue).shortValue();
                            }

                        } else if (returnedValue instanceof Character) {
                            valueToReturn = (short) (((Character) returnedValue).charValue());

                        } else if (returnedValue instanceof Boolean) {
                            if ((Boolean) returnedValue)
                                valueToReturn = 1;
                            else
                                valueToReturn = 0;

                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = ((Integer) ((Enum) returnedValue).ordinal()).shortValue();

                        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
                            valueToReturn = (short) ((Date) returnedValue).getTime();

                        } else {
                            valueToReturn = Short.parseShort(NumberUtil.removeTrailingDecimalZeros(returnedValue));
                        }

                        return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    long longValue = Long.parseLong(NumberUtil.removeTrailingDecimalZeros(valueToProcess));

                    if (longValue > Short.MAX_VALUE) {
                        valueToReturn = Short.MAX_VALUE;
                    } else {
                        valueToReturn = (short) longValue;
                    }

                    // valueToReturn = Short.parseShort(valueToProcess);
                }

                if (valueToReturn != null) {
                    Long min = objectDTO.getMin();
                    Long max = objectDTO.getMax();

                    if (min != null && min.shortValue() > valueToReturn) {
                        return min.shortValue();
                    }

                    if (max != null && max.shortValue() < valueToReturn) {
                        valueToReturn = max.shortValue();
                    }

                    return valueToReturn;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        return json2ShortDefault(objectDTO);
    }

    private <E> String short2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (returnType != null && value != null && (returnType == short.class || returnType == Short.class)) {
            Short valueToProcess = null;
            String valueToReturn = null;

            if (returnType == short.class) {
                valueToProcess = Short.valueOf((short) value);
            } else {
                valueToProcess = (Short) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Short2JsonFunction) {
                                return ((Short2JsonFunction) function).apply(valueToProcess);

                            } else {

                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Short) {
                                    valueToProcess = (Short) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    if (valueToProcess != null) {
                        Long min = objectDTO.getMin();
                        Long max = objectDTO.getMax();

                        if (min != null && min.shortValue() > valueToProcess) {
                            valueToProcess = min.shortValue();
                        }

                        if (max != null && max.shortValue() < valueToProcess) {
                            valueToProcess = max.shortValue();
                        }

                        Integer precision = objectDTO.getPrecision();
                        if (precision != null) {
                            valueToProcess = (Short) NumberUtil.setPrecision(valueToProcess, precision,
                                    getRoundingMode());
                        }

                        return NumberUtil.toPlainString(valueToProcess);
                    }

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return short2JsonDefault(objectDTO);
    }

    private String short2JsonDefault(FieldData objectDTO) {
        Short valueToReturn = json2ShortDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E> Short json2ShortDefault(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == short.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Short defaultValue = (Short) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                if (min != null && min.shortValue() > defaultValue) {
                    return min.shortValue();
                }

                return defaultValue;
            }

            if (min != null && min.shortValue() > DefaultValue.dshort) {
                return min.shortValue();
            }

            return DefaultValue.dshort;
        }

        return null;
    }

    private <E> String json2String(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        String valueToProcess = null;
        if (value != null) {
            valueToProcess = value.toString().trim();

            if (valueToProcess.length() == 0) {
                return valueToProcess;
            }
        }

        if (value != null) {
            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2StringFunction) {
                            return ((Json2StringFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (returnedValue instanceof String) {
                            valueToProcess = (String) returnedValue;

                        } else {
                            objectDTO.valueToProcess = returnedValue;
                            valueToProcess = object2String(objectDTO);
                        }

                        return valueToProcess;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }

                if (valueToProcess != null) {
                    Integer length = objectDTO.getLength();

                    if (length != null && length < valueToProcess.length()) {
                        valueToProcess = valueToProcess.substring(0, length);
                    }

                    return StringUtil.unquote(valueToProcess, isEscapeHtml());
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }
        }

        return json2StringDefault(objectDTO);
    }

    private <E> String string2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        String valueToProcess = null;

        if (value != null) {
            valueToProcess = value.toString().trim();

            if (valueToProcess.length() == 0) {
                return valueToProcess;
            }
        }

        if (valueToProcess != null) {
            try {
                Function function = objectDTO.getSerializer();
                if (function != null) {
                    try {
                        if (function instanceof DataMapper2JsonFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            return ((DataMapper2JsonFunction) function).apply(classData);

                        } else if (function instanceof String2JsonFunction) {
                            valueToProcess = ((String2JsonFunction) function).apply(valueToProcess);

                        } else {

                            Object returnedValue = null;
                            if (function instanceof FieldData2JsonFunction) {
                                FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                FieldData fieldData = objectDTO.clone();
                                returnedValue = f.apply(fieldData);
                            } else {
                                returnedValue = function.apply(value);
                            }

                            if (returnedValue == null) {
                                return null;

                            } else if (returnedValue instanceof String) {
                                valueToProcess = (String) returnedValue;

                            } else {
                                objectDTO.valueToProcess = returnedValue;
                                valueToProcess = object2String(objectDTO);
                            }

                        }

                    } catch (Exception e) {
                    }
                }

                if (valueToProcess != null) {
                    if (isMapListArrayObject(valueToProcess)) {
                        return valueToProcess;
                    }

                    Integer length = objectDTO.getLength();

                    if (length != null && length < valueToProcess.length()) {
                        valueToProcess = valueToProcess.substring(0, length);
                    }

                    return valueToProcess;
                }

            } catch (Exception ex) {
                //ex.printStackTrace();
            }
        }

        return string2JsonDefault(objectDTO);
    }

    private String string2JsonDefault(FieldData objectDTO) {
        String valueToReturn = json2StringDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E> String json2StringDefault(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == char.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            String defaultValue = (String) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                return defaultValue;
            }

            return DefaultValue.string;
        }

        return null;
    }

    private <E> Object json2Boolean(FieldData objectDTO) {
        if (objectDTO == null || !objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (value != null && value.toString().trim().length() > 0) {
            String valueToProcess = value.toString().trim();
            Boolean valueToReturn = null;

            try {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2BooleanFunction) {
                            return ((Json2BooleanFunction) function).apply(valueToProcess);
                        } else {
                            returnedValue = function.apply(valueToProcess);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return json2BooleanDefault(objectDTO);

                        } else if (returnedValue instanceof Boolean || returnedValue.getClass() == boolean.class) {
                            valueToReturn = (Boolean) returnedValue;

                        } else if (returnedValue instanceof Character || returnedValue.getClass() == char.class) {
                            char c = (Character) returnedValue;

                            valueToReturn = BooleanUtil.char2Boolean((char) returnedValue);

                        } else if (returnedValue instanceof String) {
                            valueToReturn = BooleanUtil.string2Boolean((String) returnedValue);

                        } else if (DefaultValue.isDefault(returnedValue, returnedValue.getClass())) {
                            valueToReturn = false;
                        } else {
                            valueToReturn = BooleanUtil.string2Boolean(returnedValue.toString());
                        }

                        // return valueToReturn;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    valueToReturn = BooleanUtil.string2Boolean(valueToProcess);
                }

                if (returnType == AtomicBoolean.class) {
                    return new AtomicBoolean(valueToReturn);
                }

                if (valueToReturn == null) {
                    return json2BooleanDefault(objectDTO);
                }

                return valueToReturn;

            } catch (Exception ex) {
                //ex.printStackTrace();
            }

        }

        if (returnType == AtomicBoolean.class && (getDefaultType() == JSON_INCLUDE.DEFAULT || objectDTO.required)) {
            return new AtomicBoolean();
        }

        return json2BooleanDefault(objectDTO);
    }

    private <E> String boolean2Json(FieldData objectDTO) {
        if (objectDTO == null || objectDTO.json2Java) {
            return null;
        }

        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;

        if (returnType != null && value != null && (returnType == boolean.class || returnType == Boolean.class
                || returnType == AtomicBoolean.class)) {
            Boolean valueToProcess = null;
            String valueToReturn = null;

            if (returnType == boolean.class) {
                // return String.valueOf((boolean)value);
                valueToProcess = Boolean.valueOf((boolean) value);

            } else if (returnType == AtomicBoolean.class) {
                valueToProcess = ((AtomicBoolean) value).get();

            } else {
                valueToProcess = (Boolean) value;
            }

            if (valueToProcess != null) {
                try {
                    Function function = objectDTO.getSerializer();
                    if (function != null) {
                        try {
                            if (function instanceof DataMapper2JsonFunction) {
                                DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                        objectDTO.level, getPrettyIndentation());
                                return ((DataMapper2JsonFunction) function).apply(classData);

                            } else if (function instanceof Boolean2JsonFunction) {
                                return ((Boolean2JsonFunction) function).apply(valueToProcess);

                            } else {
                                Object returnedValue = null;
                                if (function instanceof FieldData2JsonFunction) {
                                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                                    FieldData fieldData = objectDTO.clone();
                                    returnedValue = f.apply(fieldData);
                                } else {
                                    returnedValue = function.apply(value);
                                }

                                if (returnedValue instanceof Optional) {
                                    returnedValue = ObjectUtil.unwrap(returnedValue);
                                }

                                if (returnedValue == null) {
                                    return null;

                                } else if (returnedValue instanceof Boolean) {
                                    valueToProcess = (Boolean) returnedValue;

                                } else {
                                    objectDTO.valueToProcess = returnedValue;
                                    return object2String(objectDTO);
                                }

                            }

                        } catch (Exception e) {
                        }
                    }

                    return Boolean.toString(valueToProcess);

                } catch (Exception ex) {
                    //ex.printStackTrace();
                }
            }
        }

        return boolean2JsonDefault(objectDTO);
    }

    private String boolean2JsonDefault(FieldData objectDTO) {
        Boolean valueToReturn = json2BooleanDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        return valueToReturn.toString();
    }

    private <E> Boolean json2BooleanDefault(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        Long min = objectDTO.getMin();
        Long max = objectDTO.getMax();
        boolean json2Java = objectDTO.json2Java;

        if (returnType == boolean.class || getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Boolean defaultValue = (Boolean) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                return defaultValue;
            }

            return DefaultValue.bool;
        }

        return null;
    }

    // deserialize only
    private <E> Enum<?> json2Enum(FieldData objectDTO) {
        objectDTO.valueToProcess = StringUtil.unquote(objectDTO.valueToProcess, isEscapeHtml());
        Object valueToProcess = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();
        Enum defaultValue = (Enum) objectDTO.defaultValue;
        boolean json2Java = objectDTO.json2Java;

        if (returnType == null || valueToProcess == null) {
            if (required) {
                return defaultValue;
            }

            return null;
        }

        String value = (String) valueToProcess;

        Class<Enum> enumType = (Class<Enum>) returnType;

        try {
            Function function = objectDTO.getDeserializer();

            if (function != null) {

                Object returnedValue = null;

                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper, objectDTO.level,
                            getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2EnumFunction) {
                    return (Enum<?>) ((Json2EnumFunction) function).apply(value);
                } else {
                    returnedValue = function.apply(value);
                }

                if (returnedValue instanceof Optional) {
                    returnedValue = ObjectUtil.unwrap(returnedValue);
                }

                if (returnedValue == null) {
                    return null;
                }

                Class type = returnedValue.getClass();

                if (Enum.class.isAssignableFrom(type)) {
                    return (Enum<?>) returnedValue;

                } else if (Number.class.isAssignableFrom(type)) {
                    int ordinal = ((Number) returnedValue).intValue();

                    for (Enum enumValue : enumType.getEnumConstants()) {
                        if (enumValue.ordinal() == ordinal) {
                            return enumValue;
                        }
                    }

                } else {
                    String name = returnedValue.toString();

                    for (Enum enumValue : enumType.getEnumConstants()) {
                        if (enumValue.toString().equalsIgnoreCase(name)
                                || enumValue.name().equalsIgnoreCase(name)) {
                            return enumValue;
                        }
                    }
                }

            }

        } catch (Exception ex) {
        }

        for (Method method : enumType.getDeclaredMethods()) {
            for (Annotation annotation : method.getDeclaredAnnotations()) {
                String aname = annotation.annotationType().getName();

                switch (aname) {
                case "com.fasterxml.jackson.annotation.JsonCreator":
                case "org.codehaus.jackson.annotate.JsonCreator":
                    return ObjectUtil.getMethodValue(null, method, value);

                case "ca.oson.json.annotation.FieldMapper":
                    ca.oson.json.annotation.FieldMapper fieldMapper = (ca.oson.json.annotation.FieldMapper) annotation;
                    if (fieldMapper.jsonCreator() != null && fieldMapper.jsonCreator() == BOOLEAN.TRUE) {
                        return ObjectUtil.getMethodValue(null, method, value);
                    }
                }
            }

        }

        String fieldName = null;
        for (Field field : enumType.getDeclaredFields()) {
            String name = null;
            ca.oson.json.annotation.FieldMapper fieldMapper = field
                    .getAnnotation(ca.oson.json.annotation.FieldMapper.class);
            if (fieldMapper != null) {
                name = fieldMapper.name();

                if (value.equalsIgnoreCase(name)) {
                    fieldName = field.getName();
                    break;
                }

            } else {
                for (Annotation annotation : field.getAnnotations()) {
                    name = ObjectUtil.getName(annotation);
                    if (value.equalsIgnoreCase(name)) {
                        fieldName = field.getName();
                        break;
                    }
                }
            }
        }
        if (fieldName != null) {
            try {
                return Enum.valueOf(enumType, fieldName.toUpperCase());
            } catch (IllegalArgumentException ex) {
            }
        }

        try {
            return Enum.valueOf(enumType, value.toUpperCase());
        } catch (IllegalArgumentException ex) {
        }

        for (Enum enumValue : enumType.getEnumConstants()) {
            if (enumValue.toString().equalsIgnoreCase(value) || enumValue.name().equalsIgnoreCase(value)) {
                return enumValue;
            }
        }

        FieldData fieldData = new FieldData(value, Integer.class, true);
        Integer ordinal = json2Integer(fieldData);

        if (ordinal != null) {
            for (Enum enumValue : enumType.getEnumConstants()) {
                if (enumValue.ordinal() == ordinal) {
                    return enumValue;
                }
            }
        }

        return null;
    }

    private synchronized void startCachedComponentTypes(ComponentType componentType) {
        this.masterClass = componentType.getClassType();
        Class[] componentClassTypes = componentType.getComponentClassType();
        cachedComponentTypes(componentType);
    }

    private synchronized void startCachedComponentTypes(Class classType) {
        if (!ObjectUtil.isBasicDataType(classType)) {
            this.masterClass = classType;
            cachedComponentTypes(classType);
        }
    }

    private ComponentType cachedComponentTypes(ComponentType componentType) {
        Class ClassType = componentType.getClassType();
        Class[] componentClassTypes = componentType.getComponentClassType();

        if (this.masterClass == null) {
            this.masterClass = ClassType;
        }
        if (this.masterClass == null) {
            return null;
        }
        ComponentType oldComponentType = cachedComponentTypes.get(masterClass);

        if (oldComponentType == null) {
            cachedComponentTypes.put(this.masterClass, componentType);

        } else if (componentClassTypes != null) {
            for (Class componentClassType : componentClassTypes) {
                oldComponentType.add(componentClassType);
            }
        }

        return oldComponentType;
    }

    private ComponentType cachedComponentTypes(Class classType) {
        if (classType == null) {
            return null;
        }

        if (this.masterClass == null) {
            this.masterClass = classType;
        }

        ComponentType oldComponentType = cachedComponentTypes.get(masterClass);

        if (oldComponentType == null) {
            oldComponentType = new ComponentType(classType);
            cachedComponentTypes.put(this.masterClass, oldComponentType);
        }

        return oldComponentType;
    }

    private ComponentType getComponentType() {
        if (this.masterClass == null) {
            return null;
        }

        return cachedComponentTypes.get(masterClass);
    }

    private <E, R> Class guessMapKeyType(FieldData newFieldData) {
        Class returnType = newFieldData.returnType;
        String toGenericString = null;
        Class fieldType = null;
        if (newFieldData.field != null) {
            toGenericString = newFieldData.field.toGenericString();
            fieldType = ObjectUtil.getComponentType(toGenericString);
        }
        if (fieldType == null && returnType != null) {
            toGenericString = returnType.toGenericString();
            fieldType = ObjectUtil.getComponentType(toGenericString);
        }
        if (fieldType == null && newFieldData.setter != null) {
            toGenericString = newFieldData.setter.toGenericString();
            fieldType = ObjectUtil.getComponentType(toGenericString);
        }

        ComponentType type = getComponentType();
        if (type == null) {
            Class enclosingtype = newFieldData.getEnclosingtype();
            if (enclosingtype != null) {
                type = cachedComponentTypes(enclosingtype);
            }
        }

        int level = newFieldData.level;

        if (fieldType != null) {
            if (type == null) {
                type = new ComponentType(newFieldData.returnType, fieldType);
                type = cachedComponentTypes(type);

            } else {
                type.add(fieldType);
            }

            if (newFieldData.returnType == Object.class) {
                newFieldData.returnType = fieldType;
            }

            return fieldType;
        }

        if (type != null) {
            ComponentType componentType = type.getComponentType();
            while (componentType != null && componentType.getComponentType() != null && level > 1) {
                componentType = componentType.getComponentType();
                level--;
            }

            if (level == 1 && componentType != null && componentType.getKeyType() != null) {
                return componentType.getKeyType();
            }

            return type.getKeyType();
        }

        return guessComponentType(newFieldData);
    }

    private <E, R> Class guessComponentType(FieldData newFieldData) {
        boolean json2Java = newFieldData.json2Java;

        if (!json2Java) {
            return newFieldData.returnType;
        } else {

            E obj = (E) newFieldData.valueToProcess;

            Class returnType = newFieldData.returnType;
            Class itemType = null;
            if (obj != null) {
                itemType = obj.getClass();
            }

            if (newFieldData.componentType != null) {
                Class classType = newFieldData.componentType.getClassType();
                Class cls = newFieldData.componentType.getMainComponentType();
                if (cls != null
                        && (ObjectUtil.isBasicDataType(cls) || ObjectUtil.isSameDataType(classType, returnType))) {
                    return cls;
                }
            }

            String returnTypeName = null;
            if (returnType != null) {
                returnTypeName = returnType.getName();
            }
            String itemTypeName = null;
            if (itemType != null) {
                itemTypeName = itemType.getName();
            }

            int returnTypeCount = 0;
            int itemTypeCount = 0;
            String toGenericString = null;
            Class fieldType = null;
            if (newFieldData.field != null) {
                toGenericString = newFieldData.field.toGenericString();
                Class[] fieldTypes = ObjectUtil.getComponentTypes(toGenericString);

                if (fieldTypes != null && fieldTypes.length > 0) {
                    if (fieldTypes.length == 1) {
                        fieldType = fieldTypes[0];
                    } else {
                        fieldType = fieldTypes[0];

                        if (newFieldData.isMapValue) {
                            fieldType = fieldTypes[1];
                        } else {
                            fieldType = fieldTypes[0];
                        }
                    }
                }

                if (returnTypeName != null && toGenericString.indexOf(returnTypeName) > -1) {
                    returnTypeCount++;
                }
                if (itemTypeName != null && toGenericString.indexOf(itemTypeName) > -1) {
                    itemTypeCount++;
                }
            }
            if (fieldType == null && returnType != null) {
                toGenericString = returnType.toGenericString();
                fieldType = ObjectUtil.getComponentType(toGenericString);
                if (returnTypeName != null && toGenericString.indexOf(returnTypeName) > -1) {
                    returnTypeCount++;
                }
                if (itemTypeName != null && toGenericString.indexOf(itemTypeName) > -1) {
                    itemTypeCount++;
                }
            }
            if (fieldType == null && newFieldData.setter != null) {
                toGenericString = newFieldData.setter.toGenericString();
                fieldType = ObjectUtil.getComponentType(toGenericString);
                if (returnTypeName != null && toGenericString.indexOf(returnTypeName) > -1) {
                    returnTypeCount++;
                }
                if (itemTypeName != null && toGenericString.indexOf(itemTypeName) > -1) {
                    itemTypeCount++;
                }

                if (fieldType == null) {
                    Class[] types = newFieldData.setter.getParameterTypes();
                    if (types != null && types.length > 0) {
                        toGenericString = types[0].toGenericString();
                        fieldType = ObjectUtil.getComponentType(toGenericString);
                        if (returnTypeName != null && toGenericString.indexOf(returnTypeName) > -1) {
                            returnTypeCount++;
                        }
                        if (itemTypeName != null && toGenericString.indexOf(itemTypeName) > -1) {
                            itemTypeCount++;
                        }
                    }
                }
            }

            ComponentType type = getComponentType();
            if (type == null) {
                Class enclosingtype = newFieldData.getEnclosingtype();
                if (enclosingtype != null) {
                    type = cachedComponentTypes(enclosingtype);
                }
            }

            int level = newFieldData.level;

            if (fieldType != null) {
                if (returnTypeCount < itemTypeCount) {
                    newFieldData.returnType = itemType;
                }

                if (type == null) {
                    type = new ComponentType(newFieldData.returnType, fieldType);
                    type = cachedComponentTypes(type);

                } else {
                    type.add(fieldType);
                }

                if (newFieldData.returnType == Object.class) {
                    newFieldData.returnType = fieldType;
                }

                return fieldType;
            }

            if (returnType != null) {
                Class comptype = returnType.getComponentType();
                if (comptype != null && !comptype.isInterface() && !Modifier.isAbstract(comptype.getModifiers())) {
                    return comptype;
                }
            }

            if ((returnType != null && Map.class.isAssignableFrom(returnType))
                    || (itemType != null && Map.class.isAssignableFrom(itemType))) {
                String className = ((Map<String, String>) obj).get(getJsonClassType());
                if (className != null && className.length() > 0) {
                    try {
                        // figure out obj's class
                        fieldType = Class.forName(className);

                        if (type == null) {
                            if (newFieldData.returnType != fieldType) {
                                type = new ComponentType(newFieldData.returnType, fieldType);
                                type = cachedComponentTypes(type);
                            }

                        } else {
                            type.add(fieldType);
                        }
                        newFieldData.returnType = fieldType;

                        return fieldType;

                    } catch (ClassNotFoundException e) {
                        // e.printStackTrace();
                    }
                }

                if (returnTypeCount > 0 && !Map.class.isAssignableFrom(returnType)
                        && !Collection.class.isAssignableFrom(returnType) && returnType != Optional.class
                        && returnType != Object.class && !returnType.isArray()) {
                    if (type != null) {
                        type.add(returnType);
                    }

                    return returnType;
                }

                if (type != null) {
                    ComponentType componentType = type.getComponentType();
                    while (componentType != null && componentType.getComponentType() != null && level > 1) {
                        componentType = componentType.getComponentType();
                        level--;
                    }

                    if (level == 1 && componentType != null && componentType.getClassType() != null) {
                        return componentType.getClassType();
                    }

                    Class[] ctypes = type.getComponentClassType();
                    float MIN_MAX_COUNT = 20.0f;
                    if (ctypes != null && ctypes.length > 0) {
                        int length = ctypes.length;

                        Map<String, R> map = (Map) obj;

                        Map<String, Class> lnames = new HashMap<>(); //names.stream().map(name -> name.toLowerCase()).collect(Collectors.toSet());
                        for (String name : map.keySet()) {
                            Object value = map.get(name);
                            if (value != null) {
                                lnames.put(name.toLowerCase(), map.get(name).getClass());
                            }
                        }
                        int maxIdx = -1;
                        float maxCount = 0.0f;
                        for (int i = 0; i < length; i++) {
                            Class ctype = ctypes[i];

                            Field[] fields = getFields(ctype);
                            if (fields != null && fields.length > 0) {
                                int count = 0;

                                for (Field field : fields) {
                                    String name = field.getName().toLowerCase();
                                    if (lnames.containsKey(name)) {
                                        count++;

                                        Class ftype = field.getType();
                                        Class mtype = lnames.get(name);
                                        if (ObjectUtil.isSameDataType(ftype, mtype)) {
                                            count++;
                                        }
                                    }
                                }
                                float currentCount = count * 100.0f / fields.length;
                                if (currentCount > maxCount) {
                                    maxCount = currentCount;
                                    maxIdx = i;
                                } else if (maxIdx == -1 && ctype.isAssignableFrom(itemType)) {
                                    maxIdx = i;
                                }
                            }
                        }

                        if (maxIdx > -1) {
                            newFieldData.returnType = ctypes[maxIdx];

                            return ctypes[maxIdx];
                        }

                        Set<Class> processed = new HashSet(Arrays.asList(ctypes));

                        // now try to locate it in all cachedComponentTypes
                        for (Entry<Class, ComponentType> entry : cachedComponentTypes.entrySet()) {
                            Class myMasterClass = entry.getKey();

                            if (myMasterClass != this.masterClass && entry.getValue() != null) {
                                ctypes = entry.getValue().getComponentClassType();
                                if (ctypes != null) {
                                    length = ctypes.length;

                                    maxCount = 0.0f;
                                    for (int i = 0; i < length; i++) {
                                        Class ctype = ctypes[i];

                                        if (!processed.contains(ctype)) {
                                            Field[] fields = getFields(ctype);
                                            if (fields != null && fields.length > 0) {
                                                int count = 0;

                                                for (Field field : fields) {
                                                    String name = field.getName();
                                                    if (name != null) {
                                                        name = name.toLowerCase();
                                                        if (lnames.containsKey(name)) {
                                                            count++;

                                                            Class ftype = field.getType();
                                                            Class mtype = lnames.get(name);
                                                            if (ObjectUtil.isSameType(ftype, mtype)) {
                                                                count++;
                                                            }
                                                        }
                                                    }
                                                }
                                                float currentCount = count * 100.0f / fields.length;
                                                if (currentCount > maxCount) {
                                                    maxCount = currentCount;
                                                    maxIdx = i;
                                                }
                                                //                                 else if (maxIdx == -1 && ctype.isAssignableFrom(itemType)) {
                                                //                                    maxIdx = i;
                                                //                                 }
                                            }
                                        }
                                    }

                                    if (maxIdx > -1 && maxCount > MIN_MAX_COUNT) {
                                        newFieldData.returnType = ctypes[maxIdx];

                                        type.add(ctypes[maxIdx]);

                                        return ctypes[maxIdx];
                                    }

                                    processed.addAll(Arrays.asList(ctypes));
                                }
                            }
                        }

                        if (ctypes != null && ctypes.length == 1) {
                            return ctypes[0];
                        }
                    }

                }

            } else if ((returnType != null
                    && (Collection.class.isAssignableFrom(returnType) || returnType.isArray()))
                    || (itemType != null && (Collection.class.isAssignableFrom(itemType) || itemType.isArray()))) {

                //  && ComponentType.class.isAssignableFrom(erasedType.getClass())
                if (type != null) {

                    ComponentType componentType = type.getComponentType();
                    while (componentType != null && componentType.getComponentType() != null && level > 1) {
                        componentType = componentType.getComponentType();
                        level--;
                    }

                    if (level == 1 && componentType != null && componentType.getClassType() != null) {
                        return componentType.getClassType();
                    }

                    Class[] ctypes = type.getComponentClassType();

                    if (ctypes != null && ctypes.length > 0) {
                        if (ctypes.length == 1) {
                            Class cmptype = ctypes[0].getComponentType();
                            if (cmptype != null
                                    && (cmptype.isArray() || Collection.class.isAssignableFrom(cmptype))) {
                                type.add(cmptype);
                            }
                            //if (!ObjectUtil.isBasicDataType(ctypes[0])) {
                            return ctypes[0];
                            //}
                        }

                        int length = ctypes.length;
                        int depth = CollectionArrayTypeGuesser.getDepth(obj);
                        Class baseType = CollectionArrayTypeGuesser.getBaseType(obj);
                        Class possible = null;
                        for (int i = 0; i < length; i++) {
                            Class ctype = ctypes[i];

                            if (ctype.isArray() || Collection.class.isAssignableFrom(ctype)) {
                                //Class compType = CollectionArrayTypeGuesser.guessElementType(collection, ctype, getJsonClassType());
                                int typedepth = CollectionArrayTypeGuesser.getDepth(ctype);
                                Class cbaseType = CollectionArrayTypeGuesser.getBaseType(ctype);
                                if (depth == typedepth) {
                                    if (ObjectUtil.isSameType(baseType, cbaseType)) {
                                        Class cmptype = ctype.getComponentType();
                                        if (cmptype.isArray() || Collection.class.isAssignableFrom(cmptype)) {
                                            type.add(cmptype);
                                        }

                                        return ctype;

                                    } else if (itemType.isAssignableFrom(ctype)
                                            || ctype.isAssignableFrom(itemType)) {
                                        possible = ctype;
                                    }
                                }
                            }

                        }

                        if (possible != null) {
                            return possible;
                        }

                    }

                }

            }
            //         else if (StringUtil.isNumeric(obj.toString())) {
            //            if (obj.toString().contains(".")) {
            //               return Double.class;
            //            } else {
            //               return Integer.class;
            //            }
            //         }

            if (type != null && type.getComponentType() != null) {
                Class classType = type.getComponentType().getClassType();
                if (classType != null && ObjectUtil.isSameDataType(classType, itemType)) {
                    return classType;
                }
            }

            return itemType;
        }
    }

    private <E> Map json2Map(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Map returnObj = (Map) objectDTO.returnObj;
        Class<Map> returnType = objectDTO.returnType;
        Map defaultValue = (Map) objectDTO.defaultValue;

        if (!StringUtil.isEmpty(value)) {
            boolean map2ListStyle = this.isMap2ListStyle();

            Map<Object, Object> values = null;
            Class valueType = value.getClass();
            if (Map.class.isAssignableFrom(valueType)) {
                try {
                    values = (Map<Object, Object>) value;
                } catch (Exception e) {
                    // values = null; // new HashMap();
                }

            } else if (List.class.isAssignableFrom(valueType)) {
                values = ArrayToJsonMap.list2Map((List) value);
                objectDTO.valueToProcess = values;
                map2ListStyle = true;

            } else {
                try {
                    values = (Map<Object, Object>) value;
                } catch (Exception e) {
                }
            }

            if (values != null && values.size() > 0) {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;

                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, values, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            fieldData.valueToProcess = values;

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2MapFunction) {
                            return ((Json2MapFunction) function).apply(values);

                        } else {
                            returnedValue = function.apply(values);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Map.class.isAssignableFrom(returnedValue.getClass())) {
                            return (Map) returnedValue;

                        } else {
                            // do not know what to do
                        }

                    } catch (Exception e) {
                    }
                }

                if (values != null && values.size() > 0) {
                    if (returnObj == null) {
                        if (defaultValue != null) {
                            returnObj = defaultValue;
                        }

                        if (returnObj == null) {
                            returnObj = newInstance(new HashMap(), returnType);
                        }

                        if (returnObj == null) {
                            returnObj = DefaultValue.map(returnType);
                        }

                        if (returnObj == null) {
                            returnObj = new HashMap();
                        }
                    }

                    objectDTO.incrLevel();
                    Class<E> componentType = guessComponentType(objectDTO); // objectDTO.getComponentType(getJsonClassType());
                    boolean isObject = ObjectUtil.isObject(componentType);

                    Collection<Object> keys = values.keySet();
                    // || getOrderByKeyAndProperties()
                    if (objectDTO.classMapper.orderByKeyAndProperties) {
                        keys = new TreeSet(keys);
                    }

                    boolean isBsonDocument = returnObj.getClass().getCanonicalName()
                            .equals("org.bson.BsonDocument");

                    //for (Entry<String, Object> entry: values.entrySet()) {
                    // Object obj = entry.getValue(); // obj.getClass()
                    for (Object key : keys) {
                        Object obj = values.get(key); // entry.getValue(); // obj.getClass()

                        Object component = null;
                        if (obj != null) {
                            FieldData newFieldData = new FieldData(obj, obj.getClass(), objectDTO.json2Java,
                                    objectDTO.level, objectDTO.set);
                            newFieldData.field = objectDTO.field;
                            newFieldData.isMapValue = true;
                            newFieldData.returnType = guessComponentType(newFieldData);
                            if (isObject) {
                                if (ObjectUtil.isBasicDataType(newFieldData.returnType)) {
                                    newFieldData.returnType = componentType;
                                }
                            }
                            newFieldData.fieldMapper = objectDTO.fieldMapper;

                            if (isBsonDocument) {
                                newFieldData.defaultValue = json2Object(newFieldData);
                                component = Json2BsonValueFunction.apply(newFieldData);
                                if (component == null) {
                                    continue;
                                }
                            } else {
                                component = json2Object(newFieldData);
                            }
                        }

                        if (component == null && getDefaultType() == JSON_INCLUDE.DEFAULT) {
                            component = getDefaultValue(componentType);
                        }

                        //String key = entry.getKey();
                        Object keyObj = null;
                        if (Map.class.isAssignableFrom(key.getClass()) || StringUtil.parenthesized((String) key)) {
                            if (Map.class.isAssignableFrom(key.getClass())) {
                                keyObj = key;
                            } else {
                                keyObj = getListMapObject((String) key);
                            }
                            FieldData newFieldData = new FieldData(keyObj, keyObj.getClass(), objectDTO.json2Java,
                                    objectDTO.level, objectDTO.set);
                            newFieldData.field = objectDTO.field;
                            newFieldData.setter = objectDTO.setter;
                            newFieldData.enclosingtype = objectDTO.enclosingtype;
                            newFieldData.returnType = guessMapKeyType(newFieldData);
                            newFieldData.fieldMapper = objectDTO.fieldMapper;
                            keyObj = json2Object(newFieldData);

                        } else {
                            FieldData newFieldData = new FieldData(key, key.getClass(), objectDTO.json2Java,
                                    objectDTO.level, objectDTO.set);
                            newFieldData.field = objectDTO.field;
                            newFieldData.setter = objectDTO.setter;
                            newFieldData.enclosingtype = objectDTO.enclosingtype;
                            newFieldData.returnType = guessMapKeyType(newFieldData);
                            newFieldData.fieldMapper = objectDTO.fieldMapper;
                            keyObj = json2Object(newFieldData);
                        }

                        if (keyObj == null) { // failed to get a correct key value, just use its key
                            returnObj.put(key, component);

                        } else {
                            returnObj.put(keyObj, component);
                        }
                    }

                    return returnObj;
                }
            } else {
                return values;
            }
        }

        return json2MapDefault(objectDTO);
    }

    private <E> String map2Json(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<?> returnType = objectDTO.returnType;

        // && ((Map) value).size() > 0
        if (value != null) {
            Map<Object, Object> map = null;
            try {
                map = (Map) value;
            } catch (Exception ex) {
            }
            Function function = objectDTO.getSerializer();
            String valueToReturn = null;

            if (function != null) {
                try {

                    // suppose to return String, but in case not, try to process
                    if (function instanceof DataMapper2JsonFunction) {
                        DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                objectDTO.level, getPrettyIndentation());
                        return ((DataMapper2JsonFunction) function).apply(classData);

                    } else if (function instanceof Map2JsonFunction) {
                        return ((Map2JsonFunction) function).apply(map);

                    } else {

                        Object returnedValue = function.apply(map);

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Map.class.isAssignableFrom(returnedValue.getClass())) {
                            // keep on processing
                            map = (Map) returnedValue;

                        } else {
                            objectDTO.valueToProcess = returnedValue;
                            return object2String(objectDTO);
                        }
                    }

                } catch (Exception ex) {
                }
            }

            String repeated = getPrettyIndentationln(objectDTO.level), pretty = getPrettySpace();
            objectDTO.incrLevel();
            String repeatedItem = getPrettyIndentationln(objectDTO.level);
            StringBuilder sbuilder = new StringBuilder();

            String startCursor;
            String endCursor;
            String separator;
            if (this.isMap2ListStyle()) {
                startCursor = "[";
                endCursor = "]";
                separator = ",";
                repeatedItem = repeatedItem + startCursor;
            } else {
                startCursor = "{";
                endCursor = "}";
                separator = ":";
            }

            try {
                if (map != null) {
                    Set<Object> names = map.keySet();

                    if (objectDTO.classMapper.orderByKeyAndProperties) {
                        // if (getOrderByKeyAndProperties()) {//LinkedHashSet
                        names = new TreeSet(names);
                    }

                    Class lastValueType = null;

                    for (Object name : names) {
                        Object v = map.get(name);

                        String str = null;
                        if (!StringUtil.isNull(v)) {
                            lastValueType = v.getClass();
                            FieldData newFieldData = new FieldData(v, lastValueType, objectDTO.json2Java,
                                    objectDTO.level, objectDTO.set);
                            newFieldData.fieldMapper = objectDTO.fieldMapper;
                            str = object2Json(newFieldData);
                        }

                        if (str == null) {
                            if (getDefaultType() == JSON_INCLUDE.DEFAULT) {
                                str = "null"; // getDefaultValue(lastValueType).toString();
                            } else {
                                continue;
                            }
                        }

                        if (name == null) {
                            switch (objectDTO.defaultType) {
                            case ALWAYS:
                                name = "null";
                            case NON_NULL:
                                continue;
                            case NON_EMPTY:
                                continue;
                            case NON_DEFAULT:
                                continue;
                            case DEFAULT:
                                name = "null";
                            default:
                                name = "null";
                            }

                            FieldData newFieldData = new FieldData(name, name.getClass(), objectDTO.json2Java,
                                    objectDTO.level, objectDTO.set);
                            newFieldData.field = objectDTO.field;
                            newFieldData.setter = objectDTO.setter;
                            newFieldData.enclosingtype = objectDTO.enclosingtype;
                            newFieldData.returnType = guessMapKeyType(newFieldData);

                            if (newFieldData.returnType == String.class) {
                                sbuilder.append(repeatedItem + StringUtil.doublequote(name) + separator + pretty);
                            } else {
                                sbuilder.append(repeatedItem + name + separator + pretty);
                            }

                        } else {
                            Class keyClass = name.getClass();

                            if (!ObjectUtil.isBasicDataType(keyClass)) {
                                FieldData newFieldData = new FieldData(name, keyClass, objectDTO.json2Java,
                                        objectDTO.level, objectDTO.set);
                                newFieldData.fieldMapper = objectDTO.fieldMapper;
                                name = object2Json(newFieldData);
                            }
                            sbuilder.append(repeatedItem + StringUtil.doublequote(name) + separator + pretty);
                        }

                        sbuilder.append(str);
                        if (this.isMap2ListStyle()) {
                            sbuilder.append("]");
                        }
                        sbuilder.append(",");
                    }
                }
            } catch (Exception e) {
                //e.printStackTrace();
            }

            String str = sbuilder.toString();
            int size = str.length();
            if (size == 0) {
                switch (objectDTO.defaultType) {
                case ALWAYS:
                    return "{}";
                case NON_NULL:
                    return "{}";
                case NON_EMPTY:
                    return null;
                case NON_DEFAULT:
                    return null;
                case DEFAULT:
                    return "{}";
                default:
                    return "{}";
                }

            } else {
                return startCursor + str.substring(0, size - 1) + repeated + endCursor;
            }
        }

        return map2JsonDefault(objectDTO);
    }

    private String map2JsonDefault(FieldData objectDTO) {
        Map valueToReturn = json2MapDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        switch (objectDTO.defaultType) {
        case ALWAYS:
            return "{}";
        case NON_NULL:
            return "{}";
        case NON_EMPTY:
            return null;
        case NON_DEFAULT:
            return null;
        case DEFAULT:
            return "{}";
        default:
            return "{}";
        }
    }

    private <E> Map json2MapDefault(FieldData objectDTO) {
        boolean required = objectDTO.required();

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Map defaultValue = (Map) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                return defaultValue;
            }

            return DefaultValue.map(objectDTO.returnType);
        }

        return null;
    }

    private <E> Collection json2Collection(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Collection<E> returnObj = (Collection<E>) objectDTO.returnObj;
        Class<Collection<E>> returnType = objectDTO.returnType;
        Collection<E> defaultValue = (Collection<E>) objectDTO.defaultValue;

        if (returnType == null) {
            if (returnType == null && returnObj != null) {
                returnType = (Class<Collection<E>>) returnObj.getClass();
            }

            if (returnType == null) {
                returnType = (Class<Collection<E>>) DefaultValue.collection(returnType).getClass();
            }

            objectDTO.returnType = returnType;
        }

        // isEmpty
        if (!StringUtil.isNull(value)) {
            Collection<E> collection = null;
            try {
                collection = (Collection<E>) value;
            } catch (Exception e) {
                collection = DefaultValue.collection(returnType); // new ArrayList();
            }

            if (collection.size() > 0) {
                Function function = objectDTO.getDeserializer();

                if (function != null) {
                    try {
                        Object returnedValue = null;
                        // suppose to return String, but in case not, try to process
                        if (function instanceof Json2DataMapperFunction) {
                            DataMapper classData = new DataMapper(returnType, collection, objectDTO.classMapper,
                                    objectDTO.level, getPrettyIndentation());
                            returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                        } else if (function instanceof Json2FieldDataFunction) {
                            Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                            FieldData fieldData = objectDTO.clone();

                            returnedValue = f.apply(fieldData);

                        } else if (function instanceof Json2CollectionFunction) {
                            return ((Json2CollectionFunction) function).apply(collection);

                        } else {
                            returnedValue = function.apply(collection);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Collection.class.isAssignableFrom(returnedValue.getClass())) {
                            return (Collection) returnedValue;

                        } else if (returnedValue.getClass().isArray()) {
                            return Arrays.asList((Object[]) returnedValue);
                        } else {
                            // do not know what to do
                        }

                    } catch (Exception e) {
                    }
                }

                if (returnObj == null) {
                    if (defaultValue != null) {
                        returnObj = defaultValue;
                    }

                    if (EnumSet.class.isAssignableFrom(returnType)) {
                        ComponentType type = getComponentType();
                        Class enm = type.getComponentType().getClassType();
                        if (enm.isEnum()) {
                            returnObj = (Collection<E>) EnumSet.allOf(enm);
                        }
                    }

                    if (returnObj == null) {
                        returnObj = newInstance(new HashMap(), returnType);
                    }

                    if (returnObj == null) {
                        returnObj = DefaultValue.collection(returnType);
                    }

                    objectDTO.returnObj = returnObj;
                }

                objectDTO.incrLevel();

                objectDTO.valueToProcess = collection;
                //Class<E> componentType = objectDTO.getComponentType(getJsonClassType());
                Class<E> componentType = guessComponentType(objectDTO);

                for (E val : collection) {
                    if (val != null) {
                        FieldData newFieldData = new FieldData(val, objectDTO.returnType, objectDTO.json2Java,
                                objectDTO.level, objectDTO.set);
                        newFieldData.componentType = objectDTO.componentType;
                        if (ObjectUtil.isSameDataType(componentType, val.getClass())
                                && ObjectUtil.isBasicDataType(val.getClass())) {
                            newFieldData.returnType = componentType;
                        } else {
                            newFieldData.returnType = guessComponentType(newFieldData);
                        }
                        newFieldData.fieldMapper = objectDTO.fieldMapper;
                        returnObj.add(json2Object(newFieldData));
                    }
                }

                if (objectDTO.classMapper.orderArrayAndList) {
                    try {
                        if (!List.class.isAssignableFrom(returnObj.getClass())) {
                            returnObj = new ArrayList(returnObj);
                        }
                        Collections.sort((List) returnObj);
                    } catch (Exception ex) {
                    }
                }

                return returnObj;

            } else {
                return collection;
            }
        }

        return json2CollectionDefault(objectDTO);
    }

    private int[] json2ArrayInt(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == int[].class) {
                return (int[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            int[] arr = new int[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = (int) NumberUtil.getNumber(Array.get(returnedValue, j), int.class);
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        int[] arr = new int[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = (int) NumberUtil.getNumber(value, int.class);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private byte[] json2ArrayByte(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == byte[].class) {
                return (byte[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            byte[] arr = new byte[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = (byte) NumberUtil.getNumber(Array.get(returnedValue, j), byte.class);
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        byte[] arr = new byte[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = (byte) NumberUtil.getNumber(value, byte.class);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private char[] json2ArrayChar(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == char[].class) {
                return (char[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            char[] arr = new char[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    Object obj = Array.get(returnedValue, j);
                    if (obj.getClass() == Character.class || obj.getClass() == char.class) {
                        arr[i] = (char) obj;

                    } else {
                        String str = StringUtil.unquote(obj.toString(), isEscapeHtml());
                        arr[i] = str.charAt(0);
                    }
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        char[] arr = new char[size];
        int i = 0;

        for (Object obj : values) {
            if (obj != null) {
                try {
                    if (obj.getClass() == Character.class || obj.getClass() == char.class) {
                        arr[i] = (char) obj;

                    } else {
                        String str = StringUtil.unquote(obj.toString(), isEscapeHtml());
                        arr[i] = str.charAt(0);
                    }
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private float[] json2ArrayFloat(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == float[].class) {
                return (float[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            float[] arr = new float[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = (float) NumberUtil.getNumber(Array.get(returnedValue, j), float.class);
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        float[] arr = new float[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = (float) NumberUtil.getNumber(value, float.class);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private double[] json2ArrayDouble(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == double[].class) {
                return (double[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            double[] arr = new double[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = (double) NumberUtil.getNumber(Array.get(returnedValue, j), double.class);
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        double[] arr = new double[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = (double) NumberUtil.getNumber(value, double.class);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private long[] json2ArrayLong(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == long[].class) {
                return (long[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            long[] arr = new long[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = (long) NumberUtil.getNumber(Array.get(returnedValue, j), long.class);
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        long[] arr = new long[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = (long) NumberUtil.getNumber(value, long.class);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private short[] json2ArrayShort(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == short[].class) {
                return (short[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            short[] arr = new short[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = (short) NumberUtil.getNumber(Array.get(returnedValue, j), short.class);
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        short[] arr = new short[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = (short) NumberUtil.getNumber(value, short.class);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private boolean[] json2ArrayBoolean(FieldData objectDTO) {
        if (objectDTO.valueToProcess == null) {
            return null;
        }

        Function function = objectDTO.getDeserializer();

        Object returnedValue = objectDTO.valueToProcess;

        if (function != null) {
            try {

                // suppose to return String, but in case not, try to process
                if (function instanceof Json2DataMapperFunction) {
                    DataMapper classData = new DataMapper(objectDTO.returnType, objectDTO.valueToProcess,
                            objectDTO.classMapper, objectDTO.level, getPrettyIndentation());
                    returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                } else if (function instanceof Json2FieldDataFunction) {
                    Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnedValue = f.apply(fieldData);

                } else if (function instanceof Json2ArrayFunction) {
                    returnedValue = ((Json2ArrayFunction) function).apply(objectDTO.valueToProcess);

                } else {
                    returnedValue = function.apply(objectDTO.valueToProcess);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        if (returnedValue == null) {
            return null;

        } else if (returnedValue.getClass().isArray()) {
            if (returnedValue.getClass() == boolean[].class) {
                return (boolean[]) returnedValue;
            }

            int size = Array.getLength(returnedValue);
            boolean[] arr = new boolean[size];
            int i = 0, j = 0;
            while (j < size) {
                try {
                    arr[i] = BooleanUtil.object2Boolean(Array.get(returnedValue, j));
                    i++;
                } catch (Exception ex) {
                }
                j++;
            }

            if (i == size) {
                return arr;
            }
            return Arrays.copyOfRange(arr, 0, i);

        } else if (!Collection.class.isAssignableFrom(returnedValue.getClass())) {
            return null;
        }

        Collection values = (Collection) returnedValue;

        int size = values.size();
        boolean[] arr = new boolean[size];
        int i = 0;

        for (Object value : values) {
            if (value != null) {
                try {
                    arr[i] = BooleanUtil.object2Boolean(value);
                    i++;
                } catch (Exception ex) {
                }
            }
        }

        if (i == size) {
            return arr;
        }
        return Arrays.copyOfRange(arr, 0, i);
    }

    private <E> Object json2Array(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        E[] returnObj = (E[]) objectDTO.returnObj;
        Class<E[]> returnType = objectDTO.returnType;
        E[] defaultValue = (E[]) objectDTO.defaultValue;

        if (returnType == null) {
            if (returnType == null && returnObj != null) {
                returnType = (Class<E[]>) returnObj.getClass();
            }

            if (returnType == null) {
                returnType = (Class<E[]>) DefaultValue.array().getClass();
            }

            objectDTO.returnType = returnType;
        }

        if (value != null && value.toString().length() > 0) {

            Function function = objectDTO.getDeserializer();

            if (function != null) {
                try {
                    Object returnedValue = null;
                    // suppose to return String, but in case not, try to process
                    if (function instanceof Json2DataMapperFunction) {
                        DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                objectDTO.level, getPrettyIndentation());
                        returnedValue = ((Json2DataMapperFunction) function).apply(classData);

                    } else if (function instanceof Json2FieldDataFunction) {
                        Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                        FieldData fieldData = objectDTO.clone();

                        returnedValue = f.apply(fieldData);

                    } else if (function instanceof Json2ArrayFunction) {
                        return (E[]) ((Json2ArrayFunction) function).apply(value);

                    } else {
                        returnedValue = function.apply(value);
                    }

                    if (returnedValue == null) {
                        return null;

                    } else if (returnedValue.getClass().isArray()) {
                        return (E[]) returnedValue;

                    } else if (Collection.class.isAssignableFrom(returnedValue.getClass())) {
                        Collection collection = (Collection) returnedValue;
                        return (E[]) collection.toArray();

                    } else {
                        // do not know what to do
                    }

                } catch (Exception e) {
                }
            }

            Collection<E> values = null;
            try {
                values = (Collection<E>) value;
                // Class<?> valueType = values.getClass();

                // int size = values.size();
                //if (size > 0) {

                if (returnObj == null) {
                    if (defaultValue != null) {
                        returnObj = defaultValue;
                    }

                    if (returnObj == null) {
                        returnObj = (E[]) newInstance(new HashMap(), returnType);
                    }

                    if (returnObj == null) {
                        returnObj = (E[]) DefaultValue.array();
                    }

                    objectDTO.returnObj = returnObj;
                }

                objectDTO.incrLevel();

                // Class<E> componentType = objectDTO.getComponentType(getJsonClassType());
                for (Object val : values) {
                    if (val != null) {
                        objectDTO.valueToProcess = val;
                        break;
                    }
                }
                Class<E> componentType = guessComponentType(objectDTO);

                E[] arr = (E[]) Array.newInstance(componentType, values.size());

                int i = 0;
                for (Object val : values) {
                    if (!StringUtil.isNull(val)) {
                        FieldData newFieldData = new FieldData(val, objectDTO.returnType, objectDTO.json2Java,
                                objectDTO.level, objectDTO.set);
                        newFieldData.componentType = objectDTO.componentType;
                        newFieldData.returnType = guessComponentType(newFieldData);
                        newFieldData.fieldMapper = objectDTO.fieldMapper;
                        E object = json2Object(newFieldData);
                        arr[i++] = object;
                    } else {
                        arr[i++] = null;
                    }
                }

                if (objectDTO.classMapper.orderArrayAndList) {
                    try {
                        Arrays.sort(arr);
                    } catch (Exception ex) {
                    }
                }

                return arr;

            } catch (Exception e) {
            }
        }

        return json2ArrayDefault(objectDTO);
    }

    private <E> String array2Json(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<?> returnType = objectDTO.returnType;

        int size = 0;
        if (value != null && (size = Array.getLength(value)) > 0) {
            Function function = objectDTO.getSerializer();
            String valueToReturn = null;

            if (function != null) {
                try {

                    // suppose to return String, but in case not, try to process
                    if (function instanceof DataMapper2JsonFunction) {
                        DataMapper classData = new DataMapper(returnType, value, objectDTO.classMapper,
                                objectDTO.level, getPrettyIndentation());
                        return ((DataMapper2JsonFunction) function).apply(classData);

                    } else if (function instanceof Array2JsonFunction) {
                        return ((Array2JsonFunction) function).apply((E[]) value);

                    } else {

                        Object returnedValue = null;
                        if (function instanceof FieldData2JsonFunction) {
                            FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                            FieldData fieldData = objectDTO.clone();
                            returnedValue = f.apply(fieldData);
                        } else {
                            returnedValue = function.apply(value);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Array.class.isAssignableFrom(returnedValue.getClass())) {
                            // keep on processing
                            value = returnedValue;
                            size = Array.getLength(value);

                        } else {
                            objectDTO.valueToProcess = returnedValue;
                            return object2String(objectDTO);
                        }
                    }

                } catch (Exception ex) {
                }
            }

            String repeated = getPrettyIndentationln(objectDTO.level);
            objectDTO.incrLevel();
            String repeatedItem = getPrettyIndentationln(objectDTO.level);
            Class ctype = objectDTO.getComponentType(getJsonClassType());

            List<String> list = new ArrayList<>();

            for (int i = 0; i < size; i++) {
                Object componentValue = Array.get(value, i);
                String str = null;
                if (componentValue != null) {
                    FieldData newFieldData = new FieldData(componentValue, componentValue.getClass(),
                            objectDTO.json2Java, objectDTO.level, objectDTO.set);
                    newFieldData.returnType = guessComponentType(newFieldData);
                    newFieldData.fieldMapper = objectDTO.fieldMapper;
                    str = object2Json(newFieldData);
                }

                if (str == null
                        && ((ctype != null && ctype.isPrimitive()) || getDefaultType() == JSON_INCLUDE.DEFAULT)) {
                    str = getDefaultValue(ctype).toString();
                }

                list.add(str);
            }

            if (objectDTO.classMapper.orderArrayAndList) {
                Collections.sort(list);
            }

            StringBuilder sbuilder = new StringBuilder();
            for (String str : list) {
                sbuilder.append(repeatedItem + str + ",");
            }

            String str = sbuilder.toString();
            size = str.length();
            if (size > 0) {
                return "[" + str.substring(0, size - 1) + repeated + "]";
            }
        }

        return array2JsonDefault(objectDTO);
    }

    private <E> String array2JsonDefault(FieldData objectDTO) {
        Object valueToReturn = json2ArrayDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        switch (objectDTO.defaultType) {
        case ALWAYS:
            return "[]";
        case NON_NULL:
            return "[]";
        case NON_EMPTY:
            return null;
        case NON_DEFAULT:
            return null;
        case DEFAULT:
            return "[]";
        default:
            return "[]";
        }
    }

    private <E> Object json2ArrayDefault(FieldData objectDTO) {
        //Object value = objectDTO.valueToProcess;
        //Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            E[] defaultValue = (E[]) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                return defaultValue;
            }

            return (E[]) DefaultValue.array();
        }

        if (objectDTO.valueToProcess != null) {
            return Array.newInstance(objectDTO.getComponentType(getJsonClassType()), 0);
        }

        return null;
    }

    /*
     * deserialize, convert Json value to Java data
     */
    @SuppressWarnings("unchecked")
    private <E> E json2Object(FieldData objectDTO) {
        // String value, Class<E> returnType
        Class<E> returnType = objectDTO.returnType;

        Object value = objectDTO.valueToProcess;
        if (StringUtil.isNull(value)) {
            if (objectDTO.required()) {
                return (E) DefaultValue.getSystemDefault(returnType);
            }

            return null;
        }

        if (returnType == null || returnType == Object.class) {
            if (StringUtil.isEmpty(value)) {
                return null;
            }

            returnType = (Class<E>) value.getClass();
            objectDTO.returnType = returnType;
        }

        // first, get the class mapper
        //if (objectDTO.fieldMapper == null && objectDTO.classMapper == null) {
        objectDTO.classMapper = getGlobalizedClassMapper(returnType);
        //}

        if (returnType == String.class) {
            return (E) json2String(objectDTO);

            //      } else if (returnType == Optional.class) {
            //         return (E) json2Optional(objectDTO);

        } else if (returnType == Character.class || returnType == char.class) {
            return (E) json2Character(objectDTO);

        } else if (returnType == Boolean.class || returnType == boolean.class
                || returnType == AtomicBoolean.class) {
            return (E) json2Boolean(objectDTO);

        } else if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()) {

            objectDTO.valueToProcess = StringUtil.unquote(objectDTO.valueToProcess, isEscapeHtml());

            if (returnType == Integer.class || returnType == int.class) {
                return (E) json2Integer(objectDTO);
            } else if (returnType == Long.class || returnType == long.class) {
                return (E) json2Long(objectDTO);

            } else if (returnType == Double.class || returnType == double.class) {
                return (E) json2Double(objectDTO);

            } else if (returnType == Byte.class || returnType == byte.class) {
                return (E) json2Byte(objectDTO);

            } else if (returnType == Short.class || returnType == short.class) {
                return (E) json2Short(objectDTO);

            } else if (returnType == Float.class || returnType == float.class) {
                return (E) json2Float(objectDTO);

            } else if (returnType == BigDecimal.class) {
                return (E) json2BigDecimal(objectDTO);

            } else if (returnType == BigInteger.class) {
                return (E) json2BigInteger(objectDTO);

            } else if (returnType == AtomicInteger.class) {
                return (E) json2AtomicInteger(objectDTO);

            } else if (returnType == AtomicLong.class) {
                return (E) json2AtomicLong(objectDTO);

            } else { // default to Double, in case no specific type is specified
                return (E) json2Double(objectDTO);
            }

        } else if (returnType.isEnum() || Enum.class.isAssignableFrom(returnType)) {
            return (E) json2Enum(objectDTO);

        } else if (returnType == Date.class || Date.class.isAssignableFrom(returnType)) {
            return (E) json2Date(objectDTO);

        } else if (returnType.isArray() || Collection.class.isAssignableFrom(returnType)) {

            Collection<E> values = null;
            Class<?> valueType = value.getClass();
            if (Collection.class.isAssignableFrom(valueType)) {
                values = (Collection<E>) value;
            } else if (valueType.isArray()) {
                values = Arrays.asList((E[]) value);
            }

            if (values == null) {
                //return null;
            } else {
                objectDTO.valueToProcess = values;
            }

            Class<E> componentType = (Class<E>) returnType.getComponentType();
            if (componentType == null) {
                objectDTO.incrLevel();
                componentType = guessComponentType(objectDTO);
                objectDTO.descLevel();
            }
            if (componentType == null) {
                componentType = CollectionArrayTypeGuesser.guessElementType(values, returnType, getJsonClassType());
            }
            if (componentType == null)
                componentType = (Class<E>) Object.class;

            if (componentType.isPrimitive()) {
                E arr = null;
                if (componentType == int.class) {
                    arr = (E) json2ArrayInt(objectDTO);

                } else if (componentType == byte.class) {
                    arr = (E) json2ArrayByte(objectDTO);
                } else if (componentType == char.class) {
                    arr = (E) json2ArrayChar(objectDTO);
                } else if (componentType == float.class) {
                    arr = (E) json2ArrayFloat(objectDTO);
                } else if (componentType == double.class) {
                    arr = (E) json2ArrayDouble(objectDTO);
                } else if (componentType == long.class) {
                    arr = (E) json2ArrayLong(objectDTO);
                } else if (componentType == short.class) {
                    arr = (E) json2ArrayShort(objectDTO);
                } else if (componentType == boolean.class) {
                    arr = (E) json2ArrayBoolean(objectDTO);
                } else {
                    // return null;
                }

                if (arr != null && objectDTO.classMapper.orderArrayAndList) {
                    Arrays.sort((E[]) arr);
                }

                return arr;

            } else if (Collection.class.isAssignableFrom(returnType)) {
                return (E) json2Collection(objectDTO);

            } else {
                return (E) json2Array(objectDTO);
            }

        } else if (returnType != Optional.class && Map.class.isAssignableFrom(returnType)) {
            return (E) json2Map(objectDTO);

        } else {
            if (objectDTO.returnObj == null) {
                return (E) deserialize2Object(value, returnType, objectDTO);

            } else {
                objectDTO.returnType = returnType;
                return (E) deserialize2Object(objectDTO);
            }
        }
    }

    private <E> String enum2Json(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> valueType = objectDTO.returnType;
        EnumType enumType = objectDTO.getEnumType();

        if (value == null) {
            return null;
        }

        Enum en = (Enum) value;

        try {
            Function function = objectDTO.getSerializer();
            if (function != null) {
                try {
                    if (function instanceof DataMapper2JsonFunction) {
                        DataMapper classData = new DataMapper(objectDTO.returnType, value, objectDTO.classMapper,
                                objectDTO.level, getPrettyIndentation());
                        return ((DataMapper2JsonFunction) function).apply(classData);

                    } else if (function instanceof Enum2JsonFunction) {
                        return ((Enum2JsonFunction) function).apply(en);

                    } else {

                        Object returnedValue = null;
                        if (function instanceof FieldData2JsonFunction) {
                            FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                            FieldData fieldData = objectDTO.clone();
                            returnedValue = f.apply(fieldData);
                        } else {
                            returnedValue = function.apply(value);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null; // just ignore it, right?
                        } else if (Enum.class.isAssignableFrom(returnedValue.getClass())) {
                            en = (Enum) returnedValue;

                        } else {
                            objectDTO.valueToProcess = returnedValue;
                            return object2String(objectDTO);
                        }

                    }

                } catch (Exception e) {
                }
            }
        } catch (Exception ex) {
        }

        String name = en.name();

        if (enumType == null || enumType == EnumType.STRING) {

            for (Method method : valueType.getDeclaredMethods()) {
                for (Annotation annotation : method.getDeclaredAnnotations()) {
                    String aname = annotation.annotationType().getName();

                    switch (aname) {
                    case "com.fasterxml.jackson.annotation.JsonValue":
                    case "org.codehaus.jackson.annotate.JsonValue":
                        return ObjectUtil.getMethodValue(en, method);

                    case "ca.oson.json.annotation.FieldMapper":
                        ca.oson.json.annotation.FieldMapper fieldMapper = (ca.oson.json.annotation.FieldMapper) annotation;
                        if (fieldMapper.jsonValue() != null && fieldMapper.jsonValue() == BOOLEAN.TRUE) {
                            return ObjectUtil.getMethodValue(en, method);
                        }
                    }
                }

            }

            for (Field field : valueType.getDeclaredFields()) {
                if (name.equalsIgnoreCase(field.getName())) {
                    ca.oson.json.annotation.FieldMapper fieldMapper = field
                            .getAnnotation(ca.oson.json.annotation.FieldMapper.class);
                    if (fieldMapper != null) {
                        String aname = fieldMapper.name();
                        if (!StringUtil.isEmpty(aname)) {
                            return aname;
                        }

                    } else {
                        for (Annotation annotation : field.getAnnotations()) {
                            String aname = ObjectUtil.getName(annotation);
                            if (!StringUtil.isEmpty(aname)) {
                                return aname;
                            }
                        }
                    }
                }
            }

            return name;
        }

        switch (enumType) {
        case STRING:
            return en.name();
        case ORDINAL:
        default:
            return "" + en.ordinal();
        }
    }

    private <E> String collection2Json(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<?> returnType = objectDTO.returnType;

        if (value != null) {
            Collection collection = (Collection) value;
            Function function = objectDTO.getSerializer();
            String valueToReturn = null;

            if (function != null) {
                try {

                    // suppose to return String, but in case not, try to process
                    if (function instanceof DataMapper2JsonFunction) {
                        DataMapper classData = new DataMapper(returnType, collection, objectDTO.classMapper,
                                objectDTO.level, getPrettyIndentation());
                        return ((DataMapper2JsonFunction) function).apply(classData);

                    } else if (function instanceof Collection2JsonFunction) {
                        return ((Collection2JsonFunction) function).apply(collection);

                    } else {

                        Object returnedValue = null;
                        if (function instanceof FieldData2JsonFunction) {
                            FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                            FieldData fieldData = objectDTO.clone();
                            returnedValue = f.apply(fieldData);
                        } else {
                            returnedValue = function.apply(value);
                        }

                        if (returnedValue instanceof Optional) {
                            returnedValue = ObjectUtil.unwrap(returnedValue);
                        }

                        if (returnedValue == null) {
                            return null;

                        } else if (Collection.class.isAssignableFrom(returnedValue.getClass())) {
                            // keep on processing
                            collection = (Collection) returnedValue;

                        } else {
                            objectDTO.valueToProcess = returnedValue;
                            return object2String(objectDTO);
                        }
                    }

                } catch (Exception ex) {
                }
            }

            String repeated = getPrettyIndentationln(objectDTO.level);
            objectDTO.incrLevel();
            String repeatedItem = getPrettyIndentationln(objectDTO.level);
            StringBuilder sbuilder = new StringBuilder();
            Class ctype = objectDTO.getComponentType(getJsonClassType());

            try {
                List<String> list = new ArrayList<>();

                for (Object s : collection) {
                    String str = null;
                    if (s != null) {
                        FieldData newFieldData = new FieldData(s, s.getClass(), objectDTO.json2Java,
                                objectDTO.level, objectDTO.set);
                        newFieldData.returnType = guessComponentType(newFieldData);
                        newFieldData.fieldMapper = objectDTO.fieldMapper;
                        str = object2Json(newFieldData);
                    }

                    if (str == null && ((ctype != null && ctype.isPrimitive())
                            || getDefaultType() == JSON_INCLUDE.DEFAULT)) {
                        str = getDefaultValue(ctype).toString();
                    }

                    list.add(str);
                }

                if (objectDTO.classMapper.orderArrayAndList) {
                    Collections.sort(list);
                }

                for (String str : list) {
                    sbuilder.append(repeatedItem + str + ",");
                }

            } catch (Exception ex) {
            }

            String str = sbuilder.toString();
            int size = str.length();
            if (size > 0) {
                return "[" + str.substring(0, size - 1) + repeated + "]";
            } else {
                switch (objectDTO.defaultType) {
                case ALWAYS:
                    return "[]";
                case NON_NULL:
                    return "[]";
                case NON_EMPTY:
                    return null;
                case NON_DEFAULT:
                    return null;
                case DEFAULT:
                    return "[]";
                default:
                    return "[]";
                }

            }
        }

        return collection2JsonDefault(objectDTO);
    }

    private String collection2JsonDefault(FieldData objectDTO) {
        Collection valueToReturn = json2CollectionDefault(objectDTO);

        if (valueToReturn == null) {
            return null;
        }

        switch (objectDTO.defaultType) {
        case ALWAYS:
            return "[]";
        case NON_NULL:
            return "[]";
        case NON_EMPTY:
            return null;
        case NON_DEFAULT:
            return null;
        case DEFAULT:
            return "[]";
        default:
            return "[]";
        }
    }

    private <E> Collection json2CollectionDefault(FieldData objectDTO) {
        Object value = objectDTO.valueToProcess;
        Class<E> returnType = objectDTO.returnType;
        boolean required = objectDTO.required();

        if (getDefaultType() == JSON_INCLUDE.DEFAULT || required) {
            Collection defaultValue = (Collection) objectDTO.getDefaultValue();
            if (defaultValue != null) {
                return defaultValue;
            }

            return DefaultValue.collection(returnType);
        }

        return null;
    }

    private <E, R> String optional2Json(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;

        value = ObjectUtil.unwraponce(value);

        objectDTO.valueToProcess = value;
        if (value != null) {
            objectDTO.returnType = (Class<R>) value.getClass();
        }

        String repeated = getPrettyIndentationln(objectDTO.level), pretty = getPrettySpace();
        objectDTO.incrLevel();
        String repeatedItem = getPrettyIndentationln(objectDTO.level);

        return "{" + repeatedItem + "\"value\":" + pretty + object2Json(objectDTO) + repeated + "}";
    }

    private String object2String(FieldData objectDTO) {
        Object returnedValue = objectDTO.valueToProcess;

        if (returnedValue == null) {
            return null;

        } else if (returnedValue instanceof String) {
            String str = (String) returnedValue;
            if (objectDTO.isJsonRawValue()) {
                return str;
            } else if (objectDTO.doubleQuote) {
                return StringUtil.doublequote(str, isEscapeHtml());
            } else {
                return str;
            }
        }

        Class type = returnedValue.getClass();

        if (type.isPrimitive()) {
            return returnedValue + "";

        } else if (Number.class.isAssignableFrom(type)) {
            return NumberUtil.toPlainString((Number) returnedValue, this.isAppendingFloatingZero());

        } else if (Enum.class.isAssignableFrom(type)) {
            return enum2Json(objectDTO);

        } else if (returnedValue instanceof Character) {
            String str = ((Character) returnedValue).toString();
            if (objectDTO.isJsonRawValue()) {
                return str;
            } else if (objectDTO.doubleQuote) {
                return StringUtil.doublequote(str, isEscapeHtml());
            } else {
                return str;
            }

        } else if (Date.class.isAssignableFrom(returnedValue.getClass())) {
            Date valueToProcess = (Date) returnedValue;

            Boolean date2Long = objectDTO.getDate2Long();

            if (date2Long != null && date2Long) {
                long longtoprocess = valueToProcess.getTime();
                return NumberUtil.toPlainString(longtoprocess);
            } else {
                String str = objectDTO.getDateFormat().format(valueToProcess);

                if (objectDTO.isJsonRawValue()) {
                    return str;
                } else if (objectDTO.doubleQuote) {
                    return StringUtil.doublequote(str, isEscapeHtml());
                } else {
                    return str;
                }
            }

        } else if (returnedValue instanceof Boolean) {
            return ((Boolean) returnedValue).toString();

        } else {

            int oldLevel = this.getLevel();
            boolean oldPrettyPrinting = this.getPrettyPrinting();

            // now change it
            this.setLevel(5);
            this.pretty(false);

            String json = null;

            objectDTO.returnType = type;

            if (Enum.class.isAssignableFrom(type)) {
                json = enum2Json(objectDTO);

            } else if (Collection.class.isAssignableFrom(type)) {
                json = collection2Json(objectDTO);

            } else if (type.isArray()) {
                json = array2Json(objectDTO);

            } else if (Map.class.isAssignableFrom(type)) {
                json = map2Json(objectDTO);

            } else {
                objectDTO.classMapper = getGlobalizedClassMapper(type);
                json = object2Serialize(objectDTO);
                objectDTO.jsonRawValue = true;
            }

            // set it back
            this.setLevel(oldLevel);
            this.pretty(oldPrettyPrinting);

            return json;
        }
    }

    private boolean isMapListArrayObject(String str) {
        if ((str.startsWith("{") && str.endsWith("}") && str.contains(this.getJsonClassType()))
                || (str.startsWith("[") && str.endsWith("]"))) {
            return true;
        }

        return false;
    }

    private <E> String processNullValue(int level) {
        if (level > 0) {
            return null;
        }

        switch (getDefaultType()) {
        case ALWAYS:
            return "null";
        case NON_NULL:
            return "";
        case NON_EMPTY:
            return "";
        case NON_DEFAULT:
            return "";
        case DEFAULT:
            return "null";
        case NONE:
            return "null";
        default:
            return "null";
        }
    }

    //, int level, Set set
    private <E, R> String object2Json(FieldData objectDTO) {
        E value = (E) objectDTO.valueToProcess;

        Class<R> returnType = objectDTO.returnType;

        if (returnType == null || returnType == Object.class) {
            if (value != null) {
                returnType = (Class<R>) value.getClass();
                objectDTO.returnType = returnType;
            }
        }

        int level = objectDTO.level;
        //      if (value == null) {
        //         if ((returnType != null && returnType.isPrimitive())  || objectDTO.required()) {
        //            Object obj = DefaultValue.getSystemDefault(returnType);
        //            if (obj != null) {
        //               return obj.toString();
        //            }
        //
        //         }
        //
        //         return processNullValue(level);
        //      }

        // first, get the class mapper
        //if (objectDTO.mapper == null && objectDTO.classMapper == null) {
        objectDTO.classMapper = getGlobalizedClassMapper(returnType);
        //}

        if (objectDTO.defaultType == null) {
            objectDTO.defaultType = getDefaultType();
        }

        if (returnType == null) {
            if (value == null) {
                return processNullValue(level);
            } else if (objectDTO.isJsonRawValue()) {
                return value.toString();
            } else {
                return StringUtil.doublequote(value, isEscapeHtml());
            }

        } else if (value instanceof Optional) {
            return optional2Json(objectDTO);

        } else if (returnType == String.class) {
            value = (E) string2Json(objectDTO);

            if (value == null) {
                return processNullValue(level);
            }

            String str = value.toString();

            if (objectDTO.isJsonRawValue()) {
                return str;
            } else {
                if (isMapListArrayObject(str)) {
                    return str;

                } else {
                    return StringUtil.doublequote(str, isEscapeHtml());
                }
            }

        } else if (Character.class.isAssignableFrom(returnType) || char.class.isAssignableFrom(returnType)) {
            String valueToReturn = character2Json(objectDTO);

            if (valueToReturn == null) {
                return processNullValue(level);
            }

            if (objectDTO.isJsonRawValue()) {
                return (String) valueToReturn;
            } else {
                return StringUtil.doublequote(valueToReturn, isEscapeHtml());
            }

        } else if (BooleanUtil.isBoolean(returnType)
                && (value == null || BooleanUtil.isBoolean(value.getClass()))) {
            String returnedValue = boolean2Json(objectDTO);

            if (returnedValue != null) {
                if (objectDTO.isJsonRawValue()) {
                    return returnedValue;

                } else if ("true".equalsIgnoreCase(returnedValue)) {
                    return "true";

                } else if ("false".equalsIgnoreCase(returnedValue)) {
                    return "false";

                } else if (StringUtil.isNumeric(returnedValue)) {
                    return returnedValue;

                } else {
                    return StringUtil.doublequote(returnedValue, isEscapeHtml());
                }
            }

            return processNullValue(level);

        } else if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()) {

            if (returnType == Number.class && value != null) {
                returnType = (Class<R>) value.getClass();
                objectDTO.returnType = returnType;
            }

            String returnedValue = null;

            if (returnType == Integer.class || returnType == int.class) {
                returnedValue = integer2Json(objectDTO);

            } else if (returnType == Long.class || returnType == long.class) {
                returnedValue = long2Json(objectDTO);

            } else if (returnType == Byte.class || returnType == byte.class) {
                returnedValue = byte2Json(objectDTO);

            } else if (returnType == Double.class || returnType == double.class) {
                returnedValue = double2Json(objectDTO);

            } else if (returnType == Short.class || returnType == short.class) {
                returnedValue = short2Json(objectDTO);

            } else if (returnType == Float.class || returnType == float.class) {
                returnedValue = float2Json(objectDTO);

            } else if (returnType == BigDecimal.class) {
                returnedValue = bigDecimal2Json(objectDTO);

            } else if (returnType == BigInteger.class) {
                returnedValue = bigInteger2Json(objectDTO);

            } else if (returnType == AtomicInteger.class) {
                returnedValue = atomicInteger2Json(objectDTO);

            } else if (returnType == AtomicLong.class) {
                returnedValue = atomicLong2Json(objectDTO);

            } else if (value != null) {
                returnedValue = double2Json(objectDTO);

            } else {
                return processNullValue(level);
            }

            if (returnedValue != null) {
                if (objectDTO.isJsonRawValue()) {
                    return returnedValue;

                } else if (StringUtil.isNumeric(returnedValue)) {
                    return returnedValue;

                } else {
                    return StringUtil.doublequote(returnedValue, isEscapeHtml());
                }
            }

            return processNullValue(level);

        } else if (Date.class.isAssignableFrom(returnType)) {
            String returnedValue = date2Json(objectDTO);

            if (returnedValue != null) {
                if (objectDTO.isJsonRawValue()) {
                    return returnedValue;

                } else if (StringUtil.isNumeric(returnedValue)) {
                    return returnedValue;

                } else {
                    return StringUtil.doublequote(returnedValue, isEscapeHtml());
                }
            }

            return processNullValue(level);

            // returnType.isEnum()  || value instanceof Enum<?>
        } else if (Enum.class.isAssignableFrom(returnType)) {

            String returnedValue = enum2Json(objectDTO);

            if (returnedValue != null) {
                if (objectDTO.isJsonRawValue()) {
                    return returnedValue;

                } else if (StringUtil.isNumeric(returnedValue)) {
                    return returnedValue;

                } else {
                    return StringUtil.doublequote(returnedValue, isEscapeHtml());
                }
            }

            return processNullValue(level);

        } else if (Collection.class.isAssignableFrom(returnType)) {
            return collection2Json(objectDTO);

        } else if (returnType.isArray()) {
            return array2Json(objectDTO);

        } else if (Map.class.isAssignableFrom(returnType)) {
            return map2Json(objectDTO);

        } else if (objectDTO.level < getLevel()) {
            if (value == null) {
                return null;
            }

            return object2Serialize(objectDTO);

        } else {
            return "{}";
        }
    }

    private <T> Method getOtherMethodByName(String name, T obj) {
        if (obj == null || name == null) {
            return null;
        }
        Class<T> valueType = (Class<T>) obj.getClass();
        if (valueType == null) {
            return null;
        }
        return getOtherMethodByName(name, valueType);
    }

    private <T> Method getOtherMethodByName(String name, Class<T> valueType) {
        Map<String, Method> otherMethods = getOtherMethods(valueType);

        if (otherMethods == null || name == null) {
            return null;
        }
        return otherMethods.get(name);
    }

    private <T> Method getSetterByName(String name, T obj) {
        if (obj == null || name == null) {
            return null;
        }
        Class<T> valueType = (Class<T>) obj.getClass();
        if (valueType == null) {
            return null;
        }
        return getSetterByName(name, valueType);
    }

    private <T> Method getSetterByName(String name, Class<T> valueType) {
        Map<String, Method> sets = getSetters(valueType);

        if (sets == null || name == null) {
            return null;
        }
        return sets.get(name);
    }

    private <T> Method getGetterByName(String name, T obj) {
        if (obj == null || name == null) {
            return null;
        }
        Class<T> valueType = (Class<T>) obj.getClass();
        if (valueType == null) {
            return null;
        }

        return getGetterByName(name, valueType);
    }

    private <T> Method getGetterByName(String name, Class<T> valueType) {
        Map<String, Method> gets = getGetters(valueType);

        if (gets == null || name == null) {
            return null;
        }
        return gets.get(name);
    }

    private <T> Map<String, Method> getSetters(T obj) {
        if (obj == null) {
            return null;
        }
        Class<T> valueType = (Class<T>) obj.getClass();

        return getSetters(valueType);
    }

    private static boolean ignored(Class valueType) {
        if (valueType == null || valueType == ClassLoader.class || valueType == Object.class
                || valueType.getName().startsWith("java.security.")
                || valueType.getName().startsWith("sun.reflect.")) {
            return true;
        }

        return false;
    }

    private <T> Map<String, Method> getSetters(Class<T> valueType) {
        String fullName = valueType.getName();

        if (ignored(valueType)) {
            return new HashMap<>();
        }

        if (!cachedMethods.containsKey(fullName)) {
            processMethods(valueType, fullName);
        }

        // return a new copy, so original copy will not get modified
        return cachedMethods.get(fullName)[METHOD.SET.value];
    }

    private <T> Map<String, Method> getGetters(T obj) {
        if (obj == null) {
            return null;
        }
        Class<T> valueType = (Class<T>) obj.getClass();

        return getGetters(valueType);
    }

    private <T> Map<String, Method> getGetters(Class<T> valueType) {
        if (ignored(valueType)) {
            return new HashMap<>();
        }

        String fullName = valueType.getName();
        if (!cachedMethods.containsKey(fullName)) {
            processMethods(valueType, fullName);
        }

        return cachedMethods.get(fullName)[METHOD.GET.value];
    }

    private <T> Map<String, Method> getOtherMethods(T obj) {
        if (obj == null) {
            return null;
        }
        Class<T> valueType = (Class<T>) obj.getClass();

        return getOtherMethods(valueType);
    }

    private <T> Map<String, Method> getOtherMethods(Class<T> valueType) {
        if (ignored(valueType)) {
            return new HashMap<>();
        }

        String fullName = valueType.getName();
        if (!cachedMethods.containsKey(fullName)) {
            processMethods(valueType, fullName);
        }

        return cachedMethods.get(fullName)[METHOD.OTHER.value];
    }

    private <T> void processMethods(Class<T> valueType, String fullName) {
        //      Stream<Method> stream = Arrays.stream(valueType.getDeclaredMethods());
        //      while (valueType != null && valueType != Object.class) {
        //         stream = Stream.concat(stream, Arrays.stream(valueType
        //               .getSuperclass().getDeclaredMethods()));
        //         valueType = (Class<T>) valueType.getSuperclass();
        //      }

        Map<String, Method> setters = new LinkedHashMap<>();
        Map<String, Method> getters = new LinkedHashMap<>();
        Map<String, Method> others = new LinkedHashMap<>();

        String name;
        boolean notSetGetOnly = !getSetGetOnly();
        boolean completed = false;
        Class classType = valueType;
        int depth = 0;
        while (!completed && classType != null && classType != Object.class) {
            //for (Method method: stream.collect(Collectors.toList())) {
            for (Method method : classType.getDeclaredMethods()) {
                name = method.getName();

                if (name.startsWith("set")) {
                    if (name.length() > 3 && method.getParameterCount() == 1) {
                        setters.put(name.substring(3).toLowerCase(), method);
                    } else {
                        others.put(name.toLowerCase(), method);
                    }

                } else if (name.startsWith("get")) {
                    if (name.length() > 3 && method.getParameterCount() == 0
                            && !void.class.equals(method.getReturnType())) {
                        getters.put(name.substring(3).toLowerCase(), method);
                    } else {
                        others.put(name.toLowerCase(), method);
                    }

                } else if (notSetGetOnly && method.getParameterCount() == 0
                        && !void.class.equals(method.getReturnType())) {
                    getters.put(name.toLowerCase(), method);

                } else if (notSetGetOnly && method.getParameterCount() == 1) {
                    setters.put(name.toLowerCase(), method);

                } else {
                    others.put(name.toLowerCase(), method);
                }
            }

            if (depth > 1 && (getters.size() > 0 || setters.size() > 0)) {
                completed = true;
            } else {
                classType = classType.getSuperclass();
                depth++;
            }
        }

        Map<String, Method>[] all = new HashMap[3];

        all[METHOD.GET.value] = getters;
        all[METHOD.SET.value] = setters;
        all[METHOD.OTHER.value] = others;
        cachedMethods.put(fullName, all);
    }

    public static <T> Field[] getFields(T obj) {
        Class<T> valueType = (Class<T>) obj.getClass();
        // try {
        // valueType = (Class<T>)Class.forName(obj.getClass().getName());
        // } catch (ClassNotFoundException e) {
        // e.printStackTrace();
        // }

        return getFields(valueType);
    }

    public static <T> Field[] getFields(Class<T> valueType) {
        if (ignored(valueType)) {
            return new Field[0];
        }

        if (cachedFields.containsKey(valueType)) {
            return cachedFields.get(valueType);
        }

        Stream<Field> stream = Arrays.stream(valueType.getDeclaredFields());
        Class valueTypeAll = valueType.getSuperclass();
        while (valueTypeAll != null && valueTypeAll != Object.class) {
            stream = Stream.concat(stream, Arrays.stream(valueTypeAll.getDeclaredFields()));
            valueTypeAll = valueTypeAll.getSuperclass();
        }

        //(Field[]) stream.distinct().toArray(size -> new Field[size])
        List<Field> uniqueFields = new ArrayList<>();
        Set<String> set = new HashSet<>();
        for (Field field : stream.collect(Collectors.toList())) {
            String name = field.getName().toLowerCase();
            Class ftype = field.getType();
            String fname = ftype.getName();
            // && ftype != valueType
            if (!set.contains(name) && !name.startsWith("this$") && ftype != Class.class && ftype != Field.class
                    && ftype != Field[].class && ftype != sun.reflect.ReflectionFactory.class
                    // && ftype != ClassLoader.class
                    && !fname.startsWith("java.security.") && !fname.startsWith("[Ljava.security.")
                    && !fname.startsWith("java.lang.Class$") && !fname.startsWith("sun.reflect.")) {
                set.add(name);
                uniqueFields.add(field);
            }
        }

        cachedFields.put(valueType, uniqueFields.toArray(new Field[uniqueFields.size()]));

        return cachedFields.get(valueType);
    }

    private ClassMapper overwriteBy(ClassMapper classMapper,
            ca.oson.json.annotation.ClassMapper classMapperAnnotation) {
        if (classMapper == null || classMapperAnnotation == null) {
            return classMapper;
        }

        if (classMapperAnnotation.ignore() != BOOLEAN.NONE) {
            classMapper.ignore = classMapperAnnotation.ignore().value();
        }

        if (classMapperAnnotation.includeClassTypeInJson() != BOOLEAN.NONE) {
            classMapper.includeClassTypeInJson = classMapperAnnotation.includeClassTypeInJson().value();
        }

        if (classMapperAnnotation.orderByKeyAndProperties() != BOOLEAN.NONE) {
            classMapper.orderByKeyAndProperties = classMapperAnnotation.orderByKeyAndProperties().value();
        }

        if (classMapperAnnotation.orderArrayAndList() != BOOLEAN.NONE) {
            classMapper.orderArrayAndList = classMapperAnnotation.orderArrayAndList().value();
        }

        if (classMapperAnnotation.useField() != BOOLEAN.NONE) {
            classMapper.useField = classMapperAnnotation.useField().value();
        }

        if (classMapperAnnotation.useAttribute() != BOOLEAN.NONE) {
            classMapper.useAttribute = classMapperAnnotation.useAttribute().value();
        }

        if (classMapperAnnotation.since() > 0) {
            classMapper.since = classMapperAnnotation.since();
        }

        if (classMapperAnnotation.until() > 0) {
            classMapper.until = classMapperAnnotation.until();
        }

        String defaultValue = classMapperAnnotation.defaultValue();
        if (defaultValue != null && defaultValue.length() > 0) {
            classMapper.defaultValue = defaultValue;
        }

        String[] ignoreFieldsWithAnnotations = classMapperAnnotation.ignoreFieldsWithAnnotations();
        if (ignoreFieldsWithAnnotations != null && ignoreFieldsWithAnnotations.length > 0) {
            if (classMapper.ignoreFieldsWithAnnotations == null) {
                classMapper.ignoreFieldsWithAnnotations = new HashSet();
            }
            for (String ignoreFieldsWithAnnotation : ignoreFieldsWithAnnotations) {
                classMapper.ignoreFieldsWithAnnotations.add(ComponentType.forName(ignoreFieldsWithAnnotation));
            }
        }

        String[] jsonIgnoreProps = classMapperAnnotation.jsonIgnoreProperties();
        if (jsonIgnoreProps != null && jsonIgnoreProps.length > 0) {
            if (classMapper.jsonIgnoreProperties == null) {
                classMapper.jsonIgnoreProperties = new HashSet();
            }
            for (String jsonIgnoreProp : jsonIgnoreProps) {
                classMapper.jsonIgnoreProperties.add(jsonIgnoreProp);
            }
        }

        String simpleDateFormat = classMapperAnnotation.simpleDateFormat();
        if (simpleDateFormat != null && simpleDateFormat.length() > 0) {
            classMapper.setSimpleDateFormat(simpleDateFormat);
        }

        String[] propertyOrders = classMapperAnnotation.propertyOrders();
        if (propertyOrders != null && propertyOrders.length > 0) {
            classMapper.propertyOrders = propertyOrders;
        }

        MODIFIER[] modifiers = classMapperAnnotation.includeFieldsWithModifiers();
        if (modifiers != null && modifiers.length > 0) {
            classMapper.includeFieldsWithModifiers = new HashSet(Arrays.asList(modifiers));
        }

        if (classMapperAnnotation.defaultType() != JSON_INCLUDE.NONE) {
            classMapper.defaultType = classMapperAnnotation.defaultType();
        }

        if (classMapperAnnotation.enumType() != ENUM_TYPE.NONE) {
            classMapper.enumType = classMapperAnnotation.enumType().value();
        }

        if (classMapperAnnotation.date2Long() != BOOLEAN.NONE) {
            classMapper.date2Long = classMapperAnnotation.date2Long().value();
        }

        if (classMapperAnnotation.escapeHtml() != BOOLEAN.NONE) {
            classMapper.escapeHtml = classMapperAnnotation.escapeHtml().value();
        }

        if (classMapperAnnotation.length() > 0) {
            classMapper.length = classMapperAnnotation.length();
        }

        if (classMapperAnnotation.precision() > 0) {
            classMapper.precision = classMapperAnnotation.precision();
        }

        if (classMapperAnnotation.scale() > 0) {
            classMapper.scale = classMapperAnnotation.scale();
        }

        if (classMapperAnnotation.min() > 0) {
            classMapper.min = classMapperAnnotation.min();
        }

        if (classMapperAnnotation.max() > 0) {
            classMapper.max = classMapperAnnotation.max();
        }

        return classMapper;
    }

    public static ClassMapper overwriteBy(ClassMapper classMapper, ClassMapper javaClassMapper) {
        if (classMapper == null || javaClassMapper == null) {
            return classMapper;
        }

        if (javaClassMapper.date2Long != null) {
            classMapper.date2Long = javaClassMapper.date2Long;
        }

        if (javaClassMapper.ignore != null) {
            classMapper.ignore = javaClassMapper.ignore;
        }

        if (javaClassMapper.includeClassTypeInJson != null) {
            classMapper.includeClassTypeInJson = javaClassMapper.includeClassTypeInJson;
        }

        if (javaClassMapper.orderByKeyAndProperties != null) {
            classMapper.orderByKeyAndProperties = javaClassMapper.orderByKeyAndProperties;
        }

        if (javaClassMapper.orderArrayAndList != null) {
            classMapper.orderArrayAndList = javaClassMapper.orderArrayAndList;
        }

        if (javaClassMapper.useAttribute != null) {
            classMapper.useAttribute = javaClassMapper.useAttribute;
        }

        if (javaClassMapper.useField != null) {
            classMapper.useField = javaClassMapper.useField;
        }

        if (javaClassMapper.constructor != null) {
            classMapper.constructor = javaClassMapper.constructor;
        }

        if (javaClassMapper.defaultType != null) {
            classMapper.defaultType = javaClassMapper.defaultType;
        }

        if (javaClassMapper.defaultValue != null) {
            classMapper.defaultValue = javaClassMapper.defaultValue;
        }

        if (javaClassMapper.deserializer != null) {
            classMapper.deserializer = javaClassMapper.deserializer;
        }

        if (javaClassMapper.enumType != null) {
            classMapper.enumType = javaClassMapper.enumType;
        }

        if (javaClassMapper.ignoreFieldsWithAnnotations != null) {
            classMapper.ignoreFieldsWithAnnotations = javaClassMapper.ignoreFieldsWithAnnotations;
        }

        if (javaClassMapper.since != null) {
            classMapper.since = javaClassMapper.since;
        }

        if (javaClassMapper.until != null) {
            classMapper.until = javaClassMapper.until;
        }

        if (javaClassMapper.includeFieldsWithModifiers != null) {
            classMapper.includeFieldsWithModifiers = javaClassMapper.includeFieldsWithModifiers;
        }

        if (javaClassMapper.jsonIgnoreProperties != null) {
            classMapper.jsonIgnoreProperties = javaClassMapper.jsonIgnoreProperties;
        }

        if (javaClassMapper.max != null) {
            classMapper.max = javaClassMapper.max;
        }

        if (javaClassMapper.min != null) {
            classMapper.min = javaClassMapper.min;
        }

        if (javaClassMapper.propertyOrders != null) {
            classMapper.propertyOrders = javaClassMapper.propertyOrders;
        }

        if (javaClassMapper.scale != null) {
            classMapper.scale = javaClassMapper.scale;
        }

        if (javaClassMapper.serializer != null) {
            classMapper.serializer = javaClassMapper.serializer;
        }

        DateFormat dateFormat = javaClassMapper.getDateFormat();
        if (dateFormat != null) {
            classMapper.setDateFormat(dateFormat);
        }

        if (javaClassMapper.length != null) {
            classMapper.length = javaClassMapper.length;
        }

        if (javaClassMapper.precision != null) {
            classMapper.precision = javaClassMapper.precision;
        }

        if (javaClassMapper.isToStringAsSerializer() != null) {
            classMapper.setToStringAsSerializer(javaClassMapper.isToStringAsSerializer());
        }

        if (javaClassMapper.jsonValueFieldName != null) {
            classMapper.jsonValueFieldName = javaClassMapper.jsonValueFieldName;
        }

        return classMapper;
    }

    private ClassMapper overwriteBy(ClassMapper classMapper, FieldMapper fieldMapper) {
        // fieldify ?
        if (fieldMapper.useAttribute != null) {
            classMapper.useAttribute = fieldMapper.useAttribute;
        }

        if (fieldMapper.useField != null) {
            classMapper.useField = fieldMapper.useField;
        }

        DateFormat dateFormat = fieldMapper.getDateFormat();
        if (dateFormat != null) {
            classMapper.setDateFormat(dateFormat);
        }

        if (fieldMapper.defaultType != null) {
            classMapper.defaultType = fieldMapper.defaultType;
        }

        if (fieldMapper.enumType != null) {
            classMapper.enumType = fieldMapper.enumType;
        }

        if (fieldMapper.date2Long != null) {
            classMapper.date2Long = fieldMapper.date2Long;
        }

        if (fieldMapper.precision != null) {
            classMapper.precision = fieldMapper.precision;
        }

        if (fieldMapper.scale != null) {
            classMapper.scale = fieldMapper.scale;
        }

        if (fieldMapper.min != null) {
            classMapper.min = fieldMapper.min;
        }

        if (fieldMapper.max != null) {
            classMapper.max = fieldMapper.max;
        }

        if (fieldMapper.length != null) {
            classMapper.length = fieldMapper.length;
        }

        if (fieldMapper.defaultValue != null) {
            classMapper.defaultValue = fieldMapper.defaultValue;
        }

        if (fieldMapper.ignore != null) {
            classMapper.ignore = fieldMapper.ignore;
        }

        if (fieldMapper.since != null) {
            classMapper.since = fieldMapper.since;
        }

        if (fieldMapper.until != null) {
            classMapper.until = fieldMapper.until;
        }

        return classMapper;
    }

    private FieldMapper overwriteBy(FieldMapper fieldMapper,
            ca.oson.json.annotation.FieldMapper fieldMapperAnnotation, ClassMapper classMapper) {
        if (fieldMapperAnnotation.length() > 0) {
            fieldMapper.length = fieldMapperAnnotation.length();
        }

        if (fieldMapperAnnotation.min() > 0) {
            fieldMapper.min = fieldMapperAnnotation.min();
        }

        if (fieldMapperAnnotation.max() > 0) {
            fieldMapper.max = fieldMapperAnnotation.max();
        }
        if (fieldMapperAnnotation.scale() > 0) {
            fieldMapper.scale = fieldMapperAnnotation.scale();
        }
        if (fieldMapperAnnotation.precision() > 0) {
            fieldMapper.precision = fieldMapperAnnotation.precision();
        }

        if (fieldMapperAnnotation.ignore() != BOOLEAN.NONE) {
            fieldMapper.ignore = fieldMapperAnnotation.ignore().value();
        }

        if (fieldMapperAnnotation.jsonRawValue() != BOOLEAN.NONE) {
            fieldMapper.jsonRawValue = fieldMapperAnnotation.jsonRawValue().value();
        }

        if (fieldMapperAnnotation.jsonValue() != BOOLEAN.NONE) {
            fieldMapper.jsonValue = fieldMapperAnnotation.jsonValue().value();
        }

        if (fieldMapperAnnotation.jsonNoName() != BOOLEAN.NONE) {
            fieldMapper.jsonNoName = fieldMapperAnnotation.jsonNoName().value();
        }

        if (fieldMapperAnnotation.jsonAnySetter() != BOOLEAN.NONE) {
            fieldMapper.jsonAnySetter = fieldMapperAnnotation.jsonAnySetter().value();
        }

        if (fieldMapperAnnotation.jsonAnyGetter() != BOOLEAN.NONE) {
            fieldMapper.jsonAnyGetter = fieldMapperAnnotation.jsonAnyGetter().value();
        }

        if (fieldMapperAnnotation.useField() != BOOLEAN.NONE) {
            fieldMapper.useField = fieldMapperAnnotation.useField().value();
        }

        if (fieldMapperAnnotation.useAttribute() != BOOLEAN.NONE) {
            fieldMapper.useAttribute = fieldMapperAnnotation.useAttribute().value();
        }

        if (fieldMapperAnnotation.defaultType() != JSON_INCLUDE.NONE) {
            fieldMapper.defaultType = fieldMapperAnnotation.defaultType();
        }

        String defaultValue = fieldMapperAnnotation.defaultValue();
        if (defaultValue != null && defaultValue.length() > 0) {
            fieldMapper.defaultValue = defaultValue;
        }

        if (fieldMapperAnnotation.required() != BOOLEAN.NONE) {
            fieldMapper.required = fieldMapperAnnotation.required().value();
        }

        if (fieldMapperAnnotation.enumType() != ENUM_TYPE.NONE) {
            fieldMapper.enumType = fieldMapperAnnotation.enumType().value();
        }

        if (fieldMapperAnnotation.date2Long() != BOOLEAN.NONE) {
            fieldMapper.date2Long = fieldMapperAnnotation.date2Long().value();
        }

        String simpleDateFormat = fieldMapperAnnotation.simpleDateFormat();
        if (simpleDateFormat != null && simpleDateFormat.length() > 0) {
            fieldMapper.setSimpleDateFormat(simpleDateFormat);
        }

        if (fieldMapperAnnotation.since() > 0) {
            fieldMapper.since = fieldMapperAnnotation.since();
        }

        if (fieldMapperAnnotation.until() > 0) {
            fieldMapper.until = fieldMapperAnnotation.until();
        }

        if (!StringUtil.isEmpty(fieldMapperAnnotation.name())) {
            fieldMapper.json = fieldMapperAnnotation.name();
        }

        return fieldMapper;
    }

    public static FieldMapper overwriteBy(FieldMapper fieldMapper, FieldMapper javaFieldMapper) {
        if (fieldMapper == null || javaFieldMapper == null) {
            return fieldMapper;
        }

        if (javaFieldMapper.date2Long != null) {
            fieldMapper.date2Long = javaFieldMapper.date2Long;
        }

        if (javaFieldMapper.ignore != null) {
            fieldMapper.ignore = javaFieldMapper.ignore;
        }

        if (javaFieldMapper.jsonAnyGetter != null) {
            fieldMapper.jsonAnyGetter = javaFieldMapper.jsonAnyGetter;
        }

        if (javaFieldMapper.jsonAnySetter != null) {
            fieldMapper.jsonAnySetter = javaFieldMapper.jsonAnySetter;
        }

        if (javaFieldMapper.jsonRawValue != null) {
            fieldMapper.jsonRawValue = javaFieldMapper.jsonRawValue;
        }

        if (javaFieldMapper.jsonValue != null) {
            fieldMapper.jsonValue = javaFieldMapper.jsonValue;
        }

        if (javaFieldMapper.jsonNoName != null) {
            fieldMapper.jsonNoName = javaFieldMapper.jsonNoName;
        }

        if (javaFieldMapper.required != null) {
            fieldMapper.required = javaFieldMapper.required;
        }

        if (javaFieldMapper.useAttribute != null) {
            fieldMapper.useAttribute = javaFieldMapper.useAttribute;
        }

        if (javaFieldMapper.useField != null) {
            fieldMapper.useField = javaFieldMapper.useField;
        }

        if (javaFieldMapper.defaultType != JSON_INCLUDE.NONE) {
            fieldMapper.defaultType = javaFieldMapper.defaultType;
        }

        if (javaFieldMapper.defaultValue != null) {
            fieldMapper.defaultValue = javaFieldMapper.defaultValue;
        }

        if (javaFieldMapper.deserializer != null) {
            fieldMapper.deserializer = javaFieldMapper.deserializer;
        }

        if (javaFieldMapper.enumType != null) {
            fieldMapper.enumType = javaFieldMapper.enumType;
        }

        if (javaFieldMapper.java != null && !javaFieldMapper.java.equals(javaFieldMapper.json)) {
            fieldMapper.java = javaFieldMapper.java;
            fieldMapper.json = javaFieldMapper.json;
        }

        if (javaFieldMapper.length != null) {
            fieldMapper.length = javaFieldMapper.length;
        }

        if (javaFieldMapper.max != null) {
            fieldMapper.max = javaFieldMapper.max;
        }

        if (javaFieldMapper.min != null) {
            fieldMapper.min = javaFieldMapper.min;
        }

        if (javaFieldMapper.scale != null) {
            fieldMapper.scale = javaFieldMapper.scale;
        }

        if (javaFieldMapper.precision != null) {
            fieldMapper.precision = javaFieldMapper.precision;
        }

        if (javaFieldMapper.serializer != null) {
            fieldMapper.serializer = javaFieldMapper.serializer;
        }

        if (javaFieldMapper.getDateFormat() != null) {
            fieldMapper.setDateFormat(javaFieldMapper.getDateFormat());
        }

        if (javaFieldMapper.since != null) {
            fieldMapper.since = javaFieldMapper.since;
        }

        if (javaFieldMapper.until != null) {
            fieldMapper.until = javaFieldMapper.until;
        }

        return fieldMapper;
    }

    /*
     * Object to string, serialize.
     *
     * It involves 10 steps to apply processing rules:
     * 1. Create a blank class mapper instance and Globalize it;
     * 2. if it is a field in previous execution, and set to inherit from previous mapping, combine with previous mapping;
     * 3. Apply annotations from other sources;
     * 4. Apply annotations from Oson;
     * 5. Apply Java configuration for this particular class;
     * 6. Create a blank field mapper instance for certain property with a returnType;
     * 7. Get the class mapper of the returnType;
     * 8. Classify this field mapper with the class mapper of the return type;
     * 9. Classify this field mapper with the class mapper created at step 5
     * 10. Apply annotations from other sources;
     * 11. Apply annotations from Oson;
     * 12. Apply Java configuration for this particular field.
     */
    private <E, R> String object2Serialize(FieldData objectDTO) {
        E obj = (E) objectDTO.valueToProcess;

        Class<R> valueType = objectDTO.returnType;

        if (obj == null) {
            return null;
        }

        // it is possible the same object shared by multiple variables inside the same enclosing object
        int hash = ObjectUtil.hashCode(obj, valueType);
        if (!objectDTO.goAhead(hash)) {
            return "{}";
        }

        ClassMapper classMapper = objectDTO.classMapper;
        // first build up the class-level processing rules
        // || (objectDTO.level == 0 && objectDTO.fieldMapper == null)
        //if (classMapper == null) {
        // 1. Create a blank class mapper instance
        classMapper = new ClassMapper(valueType);

        // 2. Globalize it
        classMapper = globalize(classMapper);
        objectDTO.classMapper = classMapper;
        //}

        if (objectDTO.fieldMapper != null && isInheritMapping()) {
            classMapper = overwriteBy(classMapper, objectDTO.fieldMapper);
        }

        FIELD_NAMING format = getFieldNaming();

        String repeated = getPrettyIndentationln(objectDTO.level), pretty = getPrettySpace();
        objectDTO.incrLevel();
        String repeatedItem = getPrettyIndentationln(objectDTO.level);

        // @Expose
        Set<String> exposed = null;
        if (isUseGsonExpose()) {
            exposed = new HashSet<>();
        }

        boolean annotationSupport = getAnnotationSupport();
        Annotation[] annotations = null;

        if (annotationSupport) {
            annotations = valueType.getAnnotations();

            ca.oson.json.annotation.ClassMapper classMapperAnnotation = null;

            // 3. Apply annotations from other sources
            for (Annotation annotation : annotations) {
                if (ignoreClass(annotation)) {
                    return null;
                }

                switch (annotation.annotationType().getName()) {
                case "ca.oson.json.annotation.ClassMapper":
                    classMapperAnnotation = (ca.oson.json.annotation.ClassMapper) annotation;
                    if (!(classMapperAnnotation.serialize() == BOOLEAN.BOTH
                            || classMapperAnnotation.serialize() == BOOLEAN.TRUE)) {
                        classMapperAnnotation = null;
                    }
                    break;

                case "ca.oson.json.annotation.ClassMappers":
                    ca.oson.json.annotation.ClassMappers classMapperAnnotations = (ca.oson.json.annotation.ClassMappers) annotation;
                    for (ca.oson.json.annotation.ClassMapper ann : classMapperAnnotations.value()) {
                        if (ann.serialize() == BOOLEAN.BOTH || ann.serialize() == BOOLEAN.TRUE) {
                            classMapperAnnotation = ann;
                            //break;
                        }
                    }
                    break;

                case "com.google.gson.annotations.Since":
                    Since since = (Since) annotation;
                    classMapper.since = since.value();
                    break;

                case "com.google.gson.annotations.Until":
                    Until until = (Until) annotation;
                    classMapper.until = until.value();
                    break;

                case "com.fasterxml.jackson.annotation.JsonIgnoreProperties":
                    JsonIgnoreProperties jsonIgnoreProperties = (JsonIgnoreProperties) annotation;
                    String[] jsonnames = jsonIgnoreProperties.value();
                    if (jsonnames != null && jsonnames.length > 0) {
                        if (classMapper.jsonIgnoreProperties == null) {
                            classMapper.jsonIgnoreProperties = new HashSet();
                        }

                        classMapper.jsonIgnoreProperties.addAll(Arrays.asList(jsonnames));
                    }
                    break;

                case "org.codehaus.jackson.annotate.JsonIgnoreProperties":
                    org.codehaus.jackson.annotate.JsonIgnoreProperties jsonIgnoreProperties2 = (org.codehaus.jackson.annotate.JsonIgnoreProperties) annotation;
                    String[] jsonnames2 = jsonIgnoreProperties2.value();
                    if (jsonnames2 != null && jsonnames2.length > 0) {
                        if (classMapper.jsonIgnoreProperties == null) {
                            classMapper.jsonIgnoreProperties = new HashSet();
                        }

                        classMapper.jsonIgnoreProperties.addAll(Arrays.asList(jsonnames2));
                    }
                    break;

                case "com.fasterxml.jackson.annotation.JsonPropertyOrder":
                    // first come first serve
                    if (classMapper.propertyOrders == null) {
                        classMapper.propertyOrders = ((JsonPropertyOrder) annotation).value();
                    }
                    break;

                case "org.codehaus.jackson.annotate.JsonPropertyOrder":
                    // first come first serve
                    if (classMapper.propertyOrders == null) {
                        classMapper.propertyOrders = ((org.codehaus.jackson.annotate.JsonPropertyOrder) annotation)
                                .value();
                    }
                    break;

                case "com.fasterxml.jackson.annotation.JsonInclude":
                    if (classMapper.defaultType == JSON_INCLUDE.NONE) {
                        JsonInclude jsonInclude = (JsonInclude) annotation;
                        switch (jsonInclude.content()) {
                        case ALWAYS:
                            classMapper.defaultType = JSON_INCLUDE.ALWAYS;
                            break;
                        case NON_NULL:
                            classMapper.defaultType = JSON_INCLUDE.NON_NULL;
                            break;
                        case NON_ABSENT:
                            classMapper.defaultType = JSON_INCLUDE.NON_NULL;
                            break;
                        case NON_EMPTY:
                            classMapper.defaultType = JSON_INCLUDE.NON_EMPTY;
                            break;
                        case NON_DEFAULT:
                            classMapper.defaultType = JSON_INCLUDE.NON_DEFAULT;
                            break;
                        case USE_DEFAULTS:
                            classMapper.defaultType = JSON_INCLUDE.DEFAULT;
                            break;
                        }
                    }
                    break;

                case "com.fasterxml.jackson.annotation.JsonAutoDetect":
                    JsonAutoDetect jsonAutoDetect = (JsonAutoDetect) annotation;
                    if (jsonAutoDetect.fieldVisibility() == Visibility.NONE) {
                        classMapper.useField = false;
                    }
                    if (jsonAutoDetect.getterVisibility() == Visibility.NONE) {
                        classMapper.useAttribute = false;
                    }
                    break;

                case "org.codehaus.jackson.annotate.JsonAutoDetect":
                    org.codehaus.jackson.annotate.JsonAutoDetect jsonAutoDetect2 = (org.codehaus.jackson.annotate.JsonAutoDetect) annotation;
                    if (jsonAutoDetect2
                            .fieldVisibility() == org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE) {
                        classMapper.useField = false;
                    }
                    if (jsonAutoDetect2
                            .getterVisibility() == org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE) {
                        classMapper.useAttribute = false;
                    }

                    break;

                case "org.junit.Ignore":
                    classMapper.ignore = true;
                    break;
                }
            }

            // 4. Apply annotations from Oson
            if (classMapperAnnotation != null) {
                classMapper = overwriteBy(classMapper, classMapperAnnotation);
                exposed = null;
            }

        }

        // 5. Apply Java configuration for this particular class
        ClassMapper javaClassMapper = getClassMapper(valueType);
        if (javaClassMapper != null) {
            classMapper = overwriteBy(classMapper, javaClassMapper);
        }

        // now processing at the class level

        if (classMapper.ignore()) {
            return null;
        }

        if (classMapper.since != null && classMapper.since > getVersion()) {
            return null;
        } else if (classMapper.until != null && classMapper.until <= getVersion()) {
            return null;
        }

        Function function = classMapper.serializer; //getSerializer(valueType);
        if (function == null) {
            function = DeSerializerUtil.getSerializer(valueType.getName());
        }

        if (function != null) {
            try {
                Object returnValue = null;
                if (function instanceof DataMapper2JsonFunction) {
                    DataMapper classData = new DataMapper(valueType, obj, classMapper, objectDTO.level,
                            getPrettyIndentation());
                    objectDTO.jsonRawValue = false;
                    DataMapper2JsonFunction f = (DataMapper2JsonFunction) function;

                    return f.apply(classData);

                } else if (function instanceof FieldData2JsonFunction) {
                    FieldData2JsonFunction f = (FieldData2JsonFunction) function;
                    FieldData fieldData = objectDTO.clone();

                    returnValue = f.apply(fieldData);

                } else {
                    returnValue = function.apply(obj);
                }

                if (returnValue != null) {
                    Class returnType = returnValue.getClass();

                    if (returnType == String.class) {
                        return StringUtil.doublequote(returnValue, isEscapeHtml());

                    } else if (returnType == valueType || valueType.isAssignableFrom(returnType)) {
                        // just continue to do the serializing
                    } else {
                        objectDTO.valueToProcess = returnValue;
                        objectDTO.returnType = returnType;

                        return object2String(objectDTO);
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        Set<Class> ignoreFieldsWithAnnotations = classMapper.ignoreFieldsWithAnnotations;

        Map<String, String> keyJsonStrings = new LinkedHashMap<>();
        // to hold relation between name and changed name
        Map<String, String> fieldNames = new LinkedHashMap<>();

        Set<String> processedNameSet = new HashSet<>();
        //StringBuffer sb = new StringBuffer();

        Map<String, Method> getters = null;
        Map<String, Method> setters = null;
        Map<String, Method> otherMethods = null;

        if (valueType.isInterface()) {
            valueType = (Class<R>) obj.getClass();
        } else if (Modifier.isAbstract(valueType.getModifiers())) {
            // valueType

        } else {
            //         Class objClass = obj.getClass();
            //         if (valueType.isAssignableFrom(objClass)) {
            //            valueType = objClass;
            //         }
        }

        //      if (valueType.isInterface()) {
        //         getters = getGetters(obj);
        //         setters = getSetters(obj);
        //         otherMethods = getOtherMethods(obj);
        //      } else {
        getters = getGetters(valueType);
        setters = getSetters(valueType);
        otherMethods = getOtherMethods(valueType);
        //      }

        Set<Method> jsonAnyGetterMethods = new HashSet<>();

        if (classMapper.isToStringAsSerializer()) {
            try {
                Method getter = valueType.getDeclaredMethod("toString", null);

                if (getter != null) {
                    E getterValue = ObjectUtil.getMethodValue(obj, getter);

                    if (getterValue != null) {
                        Class returnType = getterValue.getClass();

                        if (returnType == String.class) {
                            return StringUtil.doublequote(getterValue, isEscapeHtml());

                        } else if (returnType == valueType || valueType.isAssignableFrom(returnType)) {
                            // just continue to do the serializing
                        } else {
                            objectDTO.valueToProcess = getterValue;
                            objectDTO.returnType = returnType;

                            return object2String(objectDTO);
                        }
                    }
                }

            } catch (NoSuchMethodException | SecurityException e) {
                // e.printStackTrace();
            }
        }

        //if (getters != null && getters.size() > 0) {
        boolean isJsonRawValue = false;
        String jsonValueFieldName = DeSerializerUtil.getJsonValueFieldName(valueType.getName());
        //         if (jsonValueFieldName == null) {
        //            // get all fieldmappers for this class?
        //            Set<FieldMapper> fieldMappers = getFieldMappers(valueType);
        //            // looking for the method
        //            for (FieldMapper fieldMapper: fieldMappers) {
        //               if (fieldMapper.jsonValue != null && fieldMapper.jsonValue) {
        //                  jsonValueFieldName = fieldMapper.java;
        //                  isJsonRawValue = fieldMapper.isJsonRawValue();
        //                  break;
        //               }
        //            }
        //         }
        if (jsonValueFieldName == null) {
            jsonValueFieldName = classMapper.getJsonValueFieldName();
        }

        if (jsonValueFieldName != null) {
            String lcjava = jsonValueFieldName.toLowerCase();
            Method getter = null;
            if (getters != null && getters.containsKey(lcjava)) {
                getter = getters.get(lcjava);

            } else {
                try {
                    getter = valueType.getMethod(jsonValueFieldName);
                } catch (NoSuchMethodException | SecurityException e) {
                    // e.printStackTrace();
                }

                if (getter == null) {
                    try {
                        getter = valueType.getMethod("get" + StringUtil.capitalize(jsonValueFieldName));
                    } catch (NoSuchMethodException | SecurityException e) {
                        //e.printStackTrace();
                    }
                }
            }

            if (getter != null) {
                E getterValue = ObjectUtil.getMethodValue(obj, getter);

                if (getterValue != null) {
                    Class returnType = getterValue.getClass();

                    if (returnType == String.class) {
                        if (isJsonRawValue) {
                            return getterValue.toString();

                        } else if (StringUtil.parenthesized(getterValue.toString())) {
                            return getterValue.toString();

                        } else {
                            return StringUtil.doublequote(getterValue, isEscapeHtml());
                        }

                    } else if (returnType == valueType || valueType.isAssignableFrom(returnType)) {
                        // just continue to do the serializing
                    } else {
                        objectDTO.valueToProcess = getterValue;
                        objectDTO.returnType = returnType;
                        objectDTO.jsonRawValue = isJsonRawValue;

                        return object2String(objectDTO);
                    }
                }
            }
        }
        //}

        try {
            Field[] fields = null;
            //         if (valueType.isInterface()) {
            //            fields = getFields(obj);
            //         } else {
            fields = getFields(valueType);
            //         }

            for (Field f : fields) {
                f.setAccessible(true);

                String name = f.getName();
                String fieldName = name;
                String lcfieldName = fieldName.toLowerCase();

                if (Modifier.isFinal(f.getModifiers()) && Modifier.isStatic(f.getModifiers())) {
                    getters.remove(lcfieldName);
                    continue;
                }

                // 6. Create a blank field mapper instance
                FieldMapper fieldMapper = new FieldMapper(name, name, valueType);

                Class<?> returnType = f.getType(); // value.getClass();

                // 7. get the class mapper of returnType
                ClassMapper fieldClassMapper = getClassMapper(returnType);

                // 8. Classify this field mapper with returnType
                fieldMapper = classifyFieldMapper(fieldMapper, fieldClassMapper);

                // 9. Classify this field mapper
                fieldMapper = classifyFieldMapper(fieldMapper, classMapper);

                FieldMapper javaFieldMapper = getFieldMapper(name, null, valueType);

                // getter and setter methods
                Method getter = getters.get(lcfieldName);
                Method setter = setters.get(lcfieldName);

                if (getter != null) {
                    getter.setAccessible(true);
                }

                // control by visibility is not always a good idea
                // here consider the visibility of field and related getter method together
                if (ignoreModifiers(f.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                    if (getter != null) {
                        if (ignoreModifiers(getter.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                            getters.remove(lcfieldName);
                            continue;
                        }
                    } else {
                        continue;
                    }
                }

                boolean ignored = false;
                Set<String> names = new HashSet<>();

                if (annotationSupport) {
                    annotations = f.getDeclaredAnnotations();//.getAnnotations();

                    // field and getter should be treated the same way, if allowed in the class level
                    // might not be 100% correct, as the useAttribute as not be applied from annotations yet
                    //  && ((javaFieldMapper == null || javaFieldMapper.useAttribute == null) && (fieldMapper.useAttribute == null || fieldMapper.useAttribute))
                    // || (javaFieldMapper != null && javaFieldMapper.useAttribute != null && javaFieldMapper.useAttribute)
                    // annotations might apply to method only, not the field, so need to get them, regardless using attribute or not
                    if (getter != null) {
                        annotations = Stream
                                .concat(Arrays.stream(annotations), Arrays.stream(getter.getDeclaredAnnotations()))
                                .toArray(Annotation[]::new);

                        // no annotations, then try set method
                        if ((annotations == null || annotations.length == 0) && setter != null) {
                            annotations = setter.getDeclaredAnnotations();
                        }
                    }

                    ca.oson.json.annotation.FieldMapper fieldMapperAnnotation = null;

                    for (Annotation annotation : annotations) {
                        if (ignoreField(annotation, ignoreFieldsWithAnnotations)) {
                            ignored = true;
                            break;

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMapper) {
                            fieldMapperAnnotation = (ca.oson.json.annotation.FieldMapper) annotation;
                            if (!(fieldMapperAnnotation.serialize() == BOOLEAN.BOTH
                                    || fieldMapperAnnotation.serialize() == BOOLEAN.TRUE)) {
                                fieldMapperAnnotation = null;
                            }

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMappers) {
                            ca.oson.json.annotation.FieldMappers fieldMapperAnnotations = (ca.oson.json.annotation.FieldMappers) annotation;
                            for (ca.oson.json.annotation.FieldMapper ann : fieldMapperAnnotations.value()) {
                                if (ann.serialize() == BOOLEAN.BOTH || ann.serialize() == BOOLEAN.TRUE) {
                                    fieldMapperAnnotation = ann;
                                    //break;
                                }
                            }

                        } else {
                            // to improve performance, using swith on string
                            switch (annotation.annotationType().getName()) {

                            case "com.fasterxml.jackson.annotation.JsonAnyGetter":
                            case "org.codehaus.jackson.annotate.JsonAnyGetter":
                                fieldMapper.jsonAnyGetter = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnore":
                            case "org.codehaus.jackson.annotate.JsonIgnore":
                                fieldMapper.ignore = true;
                                break;

                            case "javax.persistence.Transient":
                                ignored = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnoreProperties":
                                JsonIgnoreProperties jsonIgnoreProperties = (JsonIgnoreProperties) annotation;
                                if (!jsonIgnoreProperties.allowGetters()) {
                                    fieldMapper.ignore = true;
                                } else {
                                    fieldMapper.ignore = false;
                                    classMapper.jsonIgnoreProperties.remove(name);
                                }
                                break;

                            case "com.google.gson.annotations.Expose":
                                Expose expose = (Expose) annotation;
                                if (!expose.serialize()) {
                                    fieldMapper.ignore = true;
                                } else if (exposed != null) {
                                    exposed.add(lcfieldName);
                                }
                                break;

                            case "com.google.gson.annotations.Since":
                                Since since = (Since) annotation;
                                fieldMapper.since = since.value();
                                break;

                            case "com.google.gson.annotations.Until":
                                Until until = (Until) annotation;
                                fieldMapper.until = until.value();
                                break;

                            case "com.fasterxml.jackson.annotation.JsonInclude":
                                if (fieldMapper.defaultType == JSON_INCLUDE.NONE) {
                                    JsonInclude jsonInclude = (JsonInclude) annotation;

                                    switch (jsonInclude.content()) {
                                    case ALWAYS:
                                        fieldMapper.defaultType = JSON_INCLUDE.ALWAYS;
                                        break;
                                    case NON_NULL:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_ABSENT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_EMPTY:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_EMPTY;
                                        break;
                                    case NON_DEFAULT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_DEFAULT;
                                        break;
                                    case USE_DEFAULTS:
                                        fieldMapper.defaultType = JSON_INCLUDE.DEFAULT;
                                        break;
                                    }
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonRawValue":
                                if (((JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "org.codehaus.jackson.annotate.JsonRawValue":
                                if (((org.codehaus.jackson.annotate.JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonValue":
                            case "org.codehaus.jackson.annotate.JsonValue":
                                fieldMapper.jsonValue = true;
                                break;

                            case "org.junit.Ignore":
                                fieldMapper.ignore = true;
                                break;

                            case "javax.persistence.Enumerated":
                                fieldMapper.enumType = ((Enumerated) annotation).value();
                                break;

                            //                  case "javax.persistence.MapKeyEnumerated":
                            //                     mapper.enumType = ((javax.persistence.MapKeyEnumerated) annotation).value();
                            //                     break;

                            case "javax.validation.constraints.NotNull":
                                fieldMapper.required = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonProperty":
                                JsonProperty jsonProperty = (JsonProperty) annotation;
                                Access access = jsonProperty.access();
                                if (access == Access.WRITE_ONLY) {
                                    fieldMapper.ignore = true;
                                    break;
                                }

                                if (jsonProperty.required()) {
                                    fieldMapper.required = true;
                                }

                                if (jsonProperty.defaultValue() != null
                                        && jsonProperty.defaultValue().length() > 0) {
                                    fieldMapper.defaultValue = jsonProperty.defaultValue();
                                }
                                break;

                            case "javax.validation.constraints.Size":
                                Size size = (Size) annotation;
                                if (size.min() > 0) {
                                    fieldMapper.min = (long) size.min();
                                }
                                if (size.max() < Integer.MAX_VALUE) {
                                    fieldMapper.max = (long) size.max();
                                }
                                break;

                            case "javax.persistence.Column":
                                Column column = (Column) annotation;
                                if (column.length() != 255) {
                                    fieldMapper.length = column.length();
                                }
                                if (column.scale() > 0) {
                                    fieldMapper.scale = column.scale();
                                }

                                if (column.precision() > 0) {
                                    fieldMapper.precision = column.precision();
                                }

                                if (!column.nullable()) {
                                    fieldMapper.required = true;
                                }

                                break;
                            }

                            String fname = ObjectUtil.getName(annotation);
                            if (!StringUtil.isEmpty(fname)) {
                                names.add(fname);
                            }
                        }
                    }

                    // 10. Apply annotations from Oson
                    // special name to handle
                    if (fieldMapperAnnotation != null) {
                        fieldMapper = overwriteBy(fieldMapper, fieldMapperAnnotation, classMapper);
                        exposed = null;
                    }
                }

                if (ignored) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                }

                // 11. Apply Java configuration for this particular field
                if (javaFieldMapper != null && javaFieldMapper.isSerializing()) {
                    fieldMapper = overwriteBy(fieldMapper, javaFieldMapper);
                }

                if (fieldMapper.ignore != null && fieldMapper.ignore) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                }

                // in the ignored list
                if (ObjectUtil.inSet(name, classMapper.jsonIgnoreProperties)) {
                    getters.remove(lcfieldName);
                    continue;
                }

                if (fieldMapper.jsonAnyGetter != null && fieldMapper.jsonAnyGetter && getter != null) {
                    getters.remove(lcfieldName);
                    jsonAnyGetterMethods.add(getter);
                    continue;
                }

                if (fieldMapper.useField != null && !fieldMapper.useField) {
                    // both should not be used, just like ignore
                    if (fieldMapper.useAttribute != null && !fieldMapper.useAttribute) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                }

                if (fieldMapper.since != null && fieldMapper.since > getVersion()) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                } else if (fieldMapper.until != null && fieldMapper.until <= getVersion()) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                }

                //jsonIgnoreProperties

                // handling name now
                boolean jnameFixed = false;
                String json = fieldMapper.json;
                if (StringUtil.isEmpty(json)) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;

                } else if (!json.equals(name)) {
                    name = json;
                    jnameFixed = true;
                }

                if (!jnameFixed) {
                    for (String jsoname : names) {
                        if (!name.equals(jsoname) && !StringUtil.isEmpty(jsoname)) {
                            name = jsoname;
                            jnameFixed = true;
                            break;
                        }
                    }
                }

                // only if the name is still the same as the field name
                // format it based on the naming settings
                // otherwise, it is set on purpose
                if (fieldName.equals(name)) {
                    name = StringUtil.formatName(name, format);
                    jnameFixed = true;
                }

                fieldMapper.java = fieldName;
                fieldMapper.json = name;

                // field valuie
                E value = null;
                try {
                    value = (E) f.get(obj);// ObjectUtil.unwraponce(f.get(obj));
                } catch (Exception e) {
                }

                if (value != null) {
                    Class vtype = value.getClass();
                    if (returnType.isAssignableFrom(vtype)) {
                        returnType = vtype;
                    }
                }

                // value from getter
                E getterValue = null;

                if (getter != null) {
                    if (fieldMapper.useAttribute == null || fieldMapper.useAttribute) {
                        getterValue = ObjectUtil.getMethodValue(obj, getter);
                        //getterValue = ObjectUtil.unwraponce(getterValue);
                    }

                    getters.remove(lcfieldName);
                }

                // determine which value to use
                if (getterValue != null) {
                    if (getterValue.equals(value) || StringUtil.isEmpty(value)) {
                        value = getterValue;

                    } else if (DefaultValue.isDefault(value, returnType)
                            && !DefaultValue.isDefault(getterValue, returnType)) {
                        value = getterValue;
                    }
                    //               else if (getterValue.toString().length() > value.toString().length()) {
                    //                  value = getterValue;
                    //               }
                }

                String str;

                FieldData fieldData = new FieldData(obj, f, value, returnType, false, fieldMapper, objectDTO.level,
                        objectDTO.set);

                str = object2Json(fieldData);

                if (fieldMapper.jsonValue != null && fieldMapper.jsonValue) {
                    if (fieldMapper.isJsonRawValue()) {
                        return StringUtil.unquote(str, isEscapeHtml());
                    } else {
                        return StringUtil.doublequote(str, isEscapeHtml());
                    }
                }

                if (StringUtil.isNull(str)) {
                    if (fieldMapper.defaultType == JSON_INCLUDE.NON_NULL
                            || fieldMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                            || fieldMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                        continue;

                    } else {
                        str = "null";
                    }

                } else if (StringUtil.isEmpty(str)) {
                    if (fieldMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                            || fieldMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                        continue;
                    }

                    str = "\"\"";

                } else if (fieldMapper.defaultType == JSON_INCLUDE.NON_DEFAULT
                        && DefaultValue.isDefault(str, returnType)) {
                    continue;
                }

                StringBuffer sb = new StringBuffer();
                sb.append(repeatedItem);
                if (fieldMapper.jsonNoName == null || !fieldMapper.jsonNoName) {
                    sb.append("\"" + name + "\":" + pretty);
                }
                sb.append(str);
                sb.append(",");

                keyJsonStrings.put(lcfieldName, sb.toString());
                processedNameSet.add(name);
                fieldNames.put(lcfieldName, name.toLowerCase());
            }

            // now process get methods
            for (Entry<String, Method> entry : getters.entrySet()) {
                String lcfieldName = entry.getKey();
                Method getter = entry.getValue();

                if (ignoreModifiers(getter.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                    continue;
                }
                if (Modifier.isFinal(getter.getModifiers()) && Modifier.isStatic(getter.getModifiers())) {
                    continue;
                }

                String name = getter.getName();
                if (name.substring(3).equalsIgnoreCase(lcfieldName)) {
                    name = StringUtil.uncapitalize(name.substring(3));
                }

                // just use field name, even it might not be a field
                String fieldName = name;

                if (processedNameSet.contains(name) || fieldNames.containsKey(lcfieldName)) {
                    continue;
                }

                getter.setAccessible(true);

                Method setter = setters.get(lcfieldName);

                // 6. Create a blank field mapper instance
                FieldMapper fieldMapper = new FieldMapper(name, name, valueType);

                Class<?> returnType = getter.getReturnType();

                // 7. get the class mapper of returnType
                ClassMapper fieldClassMapper = getClassMapper(returnType);

                // 8. Classify this field mapper with returnType
                fieldMapper = classifyFieldMapper(fieldMapper, fieldClassMapper);

                // 9. Classify this field mapper
                fieldMapper = classifyFieldMapper(fieldMapper, classMapper);

                FieldMapper javaFieldMapper = getFieldMapper(name, null, valueType);

                boolean ignored = false;
                Set<String> names = new HashSet<>();

                if (annotationSupport) {
                    annotations = getter.getDeclaredAnnotations();//.getAnnotations();

                    // no annotations, then try set method
                    if ((annotations == null || annotations.length == 0) && setter != null) {
                        annotations = setter.getDeclaredAnnotations();
                    }

                    ca.oson.json.annotation.FieldMapper fieldMapperAnnotation = null;

                    for (Annotation annotation : annotations) {
                        if (ignoreField(annotation, ignoreFieldsWithAnnotations)) {
                            ignored = true;
                            break;

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMapper) {
                            fieldMapperAnnotation = (ca.oson.json.annotation.FieldMapper) annotation;
                            if (!(fieldMapperAnnotation.serialize() == BOOLEAN.BOTH
                                    || fieldMapperAnnotation.serialize() == BOOLEAN.TRUE)) {
                                fieldMapperAnnotation = null;
                            }

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMappers) {
                            ca.oson.json.annotation.FieldMappers fieldMapperAnnotations = (ca.oson.json.annotation.FieldMappers) annotation;
                            for (ca.oson.json.annotation.FieldMapper ann : fieldMapperAnnotations.value()) {
                                if (ann.serialize() == BOOLEAN.BOTH || ann.serialize() == BOOLEAN.TRUE) {
                                    fieldMapperAnnotation = ann;
                                    //break;
                                }
                            }

                        } else {
                            // to improve performance, using swith on string
                            switch (annotation.annotationType().getName()) {
                            case "com.fasterxml.jackson.annotation.JsonAnyGetter":
                            case "org.codehaus.jackson.annotate.JsonAnyGetter":
                                fieldMapper.jsonAnyGetter = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnore":
                            case "org.codehaus.jackson.annotate.JsonIgnore":
                                fieldMapper.ignore = true;
                                break;

                            case "javax.persistence.Transient":
                                ignored = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnoreProperties":
                                JsonIgnoreProperties jsonIgnoreProperties = (JsonIgnoreProperties) annotation;
                                if (!jsonIgnoreProperties.allowGetters()) {
                                    fieldMapper.ignore = true;
                                } else {
                                    fieldMapper.ignore = false;
                                    classMapper.jsonIgnoreProperties.remove(name);
                                }
                                break;

                            case "com.google.gson.annotations.Expose":
                                Expose expose = (Expose) annotation;
                                if (!expose.serialize()) {
                                    fieldMapper.ignore = true;
                                } else if (exposed != null) {
                                    exposed.add(lcfieldName);
                                }
                                break;

                            case "com.google.gson.annotations.Since":
                                Since since = (Since) annotation;
                                fieldMapper.since = since.value();
                                break;

                            case "com.google.gson.annotations.Until":
                                Until until = (Until) annotation;
                                fieldMapper.until = until.value();
                                break;

                            case "com.fasterxml.jackson.annotation.JsonInclude":
                                if (fieldMapper.defaultType == JSON_INCLUDE.NONE) {
                                    JsonInclude jsonInclude = (JsonInclude) annotation;

                                    switch (jsonInclude.content()) {
                                    case ALWAYS:
                                        fieldMapper.defaultType = JSON_INCLUDE.ALWAYS;
                                        break;
                                    case NON_NULL:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_ABSENT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_EMPTY:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_EMPTY;
                                        break;
                                    case NON_DEFAULT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_DEFAULT;
                                        break;
                                    case USE_DEFAULTS:
                                        fieldMapper.defaultType = JSON_INCLUDE.DEFAULT;
                                        break;
                                    }
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonRawValue":
                                if (((JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "org.codehaus.jackson.annotate.JsonRawValue":
                                if (((org.codehaus.jackson.annotate.JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonValue":
                            case "org.codehaus.jackson.annotate.JsonValue":
                                fieldMapper.jsonValue = true;
                                break;

                            case "javax.persistence.Enumerated":
                                fieldMapper.enumType = ((Enumerated) annotation).value();
                                break;

                            //                  case "javax.persistence.MapKeyEnumerated":
                            //                     mapper.enumType = ((javax.persistence.MapKeyEnumerated) annotation).value();
                            //                     break;

                            case "javax.validation.constraints.NotNull":
                                fieldMapper.required = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonProperty":
                                JsonProperty jsonProperty = (JsonProperty) annotation;
                                Access access = jsonProperty.access();
                                if (access == Access.WRITE_ONLY) {
                                    fieldMapper.ignore = true;
                                    break;
                                }

                                if (jsonProperty.required()) {
                                    fieldMapper.required = true;
                                }

                                if (jsonProperty.defaultValue() != null
                                        && jsonProperty.defaultValue().length() > 0) {
                                    fieldMapper.defaultValue = jsonProperty.defaultValue();
                                }
                                break;

                            case "org.junit.Ignore":
                                fieldMapper.ignore = true;
                                break;

                            case "javax.validation.constraints.Size":
                                Size size = (Size) annotation;
                                if (size.min() > 0) {
                                    fieldMapper.min = (long) size.min();
                                }
                                if (size.max() < Integer.MAX_VALUE) {
                                    fieldMapper.max = (long) size.max();
                                }
                                break;

                            case "javax.persistence.Column":
                                Column column = (Column) annotation;
                                if (column.length() != 255) {
                                    fieldMapper.length = column.length();
                                }
                                if (column.scale() > 0) {
                                    fieldMapper.scale = column.scale();
                                }

                                if (column.precision() > 0) {
                                    fieldMapper.precision = column.precision();
                                }

                                if (!column.nullable()) {
                                    fieldMapper.required = true;
                                }

                                break;
                            }

                            String fname = ObjectUtil.getName(annotation);
                            if (fname != null) {
                                names.add(fname);
                            }
                        }
                    }

                    // 10. Apply annotations from Oson
                    // special name to handle
                    if (fieldMapperAnnotation != null) {
                        fieldMapper = overwriteBy(fieldMapper, fieldMapperAnnotation, classMapper);
                        exposed = null;
                    }
                }

                if (ignored) {
                    continue;
                }

                // 11. Apply Java configuration for this particular field
                if (javaFieldMapper != null && javaFieldMapper.isSerializing()) {
                    fieldMapper = overwriteBy(fieldMapper, javaFieldMapper);
                }

                if (fieldMapper.ignore != null && fieldMapper.ignore) {
                    continue;
                }

                // in the ignored list
                if (ObjectUtil.inSet(name, classMapper.jsonIgnoreProperties)) {
                    continue;
                }

                if (fieldMapper.jsonAnyGetter != null && fieldMapper.jsonAnyGetter) {
                    jsonAnyGetterMethods.add(getter);
                    continue;
                }

                if (fieldMapper.useAttribute != null && !fieldMapper.useAttribute) {
                    continue;
                }

                if (fieldMapper.since != null && fieldMapper.since > getVersion()) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                } else if (fieldMapper.until != null && fieldMapper.until <= getVersion()) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                }

                // handling name now
                boolean jnameFixed = false;
                String json = fieldMapper.json;
                if (StringUtil.isEmpty(json)) {
                    if (getter != null) {
                        getters.remove(lcfieldName);
                    }
                    continue;

                } else if (!json.equals(name)) {
                    name = json;
                    jnameFixed = true;
                }

                if (!jnameFixed) {
                    for (String jsoname : names) {
                        if (!name.equals(jsoname) && !StringUtil.isEmpty(jsoname)) {
                            name = jsoname;
                            jnameFixed = true;
                            break;
                        }
                    }
                }

                // only if the name is still the same as the field name
                // format it based on the naming settings
                // otherwise, it is set on purpose
                if (fieldName.equals(name)) {
                    name = StringUtil.formatName(name, format);
                    jnameFixed = true;
                }

                fieldMapper.java = fieldName;
                fieldMapper.json = name;

                // get value
                E value = ObjectUtil.getMethodValue(obj, getter);

                if (fieldMapper.jsonValue != null && fieldMapper.jsonValue) {
                    if (value != null) {
                        if (fieldMapper.isJsonRawValue()) {
                            return value.toString();
                        } else {
                            return StringUtil.doublequote(value, isEscapeHtml());
                        }
                    }
                }

                if (returnType == Class.class) {
                    if (value != null && returnType != value.getClass()) {
                        returnType = value.getClass();
                    } else {
                        continue;
                    }
                }

                String str = null;

                //if (returnType != valueType) {
                FieldData fieldData = new FieldData(obj, null, value, returnType, false, fieldMapper,
                        objectDTO.level, objectDTO.set);
                objectDTO.getter = getter;
                str = object2Json(fieldData);
                //}

                if (StringUtil.isNull(str)) {
                    if (fieldMapper.defaultType == JSON_INCLUDE.NON_NULL
                            || fieldMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                            || fieldMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                        continue;

                    } else {
                        str = "null";
                    }

                } else if (StringUtil.isEmpty(str)) {
                    if (fieldMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                            || fieldMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                        continue;
                    }

                    str = "null";

                } else if (fieldMapper.defaultType == JSON_INCLUDE.NON_DEFAULT
                        && DefaultValue.isDefault(str, returnType)) {
                    continue;
                }

                StringBuffer sb = new StringBuffer();
                sb.append(repeatedItem);
                if (fieldMapper.jsonNoName == null || !fieldMapper.jsonNoName) {
                    sb.append("\"" + name + "\":" + pretty);
                }
                sb.append(str);
                sb.append(",");

                keyJsonStrings.put(lcfieldName, sb.toString());
                processedNameSet.add(name);
                fieldNames.put(lcfieldName, name.toLowerCase());
            }

            // handle @JsonAnyGetter
            if (annotationSupport) {
                for (Entry<String, Method> entry : otherMethods.entrySet()) {
                    Method method = entry.getValue();
                    if (ignoreModifiers(method.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                        continue;
                    }

                    for (Annotation annotation : method.getAnnotations()) {
                        if (ignoreField(annotation, ignoreFieldsWithAnnotations)) {
                            continue;
                        }

                        if (annotation instanceof JsonValue
                                || annotation instanceof org.codehaus.jackson.annotate.JsonValue) {
                            Object mvalue = ObjectUtil.getMethodValue(obj, method);
                            if (mvalue != null) {
                                return StringUtil.doublequote(mvalue, isEscapeHtml());
                            }

                        } else if (annotation instanceof JsonAnyGetter
                                || annotation instanceof org.codehaus.jackson.annotate.JsonAnyGetter
                                || annotation instanceof ca.oson.json.annotation.FieldMapper) {

                            if (annotation instanceof ca.oson.json.annotation.FieldMapper) {
                                ca.oson.json.annotation.FieldMapper fieldMapper = (ca.oson.json.annotation.FieldMapper) annotation;

                                if (fieldMapper.jsonAnyGetter() == BOOLEAN.FALSE) {
                                    continue;
                                }
                            }

                            jsonAnyGetterMethods.add(method);
                        }
                    }
                }
            }

            for (Method method : jsonAnyGetterMethods) {
                if (method != null) {
                    Object allValues = ObjectUtil.getMethodValue(obj, method);

                    if (allValues != null && allValues instanceof Map) {
                        Map<String, Object> map = (Map) allValues;
                        String str;
                        for (String name : map.keySet()) {
                            Object value = map.get(name);

                            // java to json, check if this name is allowed or changed
                            name = java2Json(name);

                            if (!StringUtil.isEmpty(name)) {

                                FieldData newFieldData = new FieldData(value, value.getClass(), false,
                                        objectDTO.level, objectDTO.set);
                                newFieldData.defaultType = classMapper.defaultType;
                                str = object2Json(newFieldData);

                                if (StringUtil.isNull(str)) {
                                    if (classMapper.defaultType == JSON_INCLUDE.NON_NULL
                                            || classMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                                            || classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                                        continue;

                                    } else {
                                        str = "null";
                                    }

                                } else if (StringUtil.isEmpty(str)) {
                                    if (classMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                                            || classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                                        continue;
                                    }

                                    str = "null";

                                } else if (DefaultValue.isDefault(str, value.getClass())) {
                                    if (classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                                        continue;
                                    }
                                }

                                StringBuffer sb = new StringBuffer();
                                sb.append(repeatedItem);
                                sb.append("\"" + name + "\":" + pretty);
                                sb.append(str);
                                sb.append(",");
                                keyJsonStrings.put(name, sb.toString());
                            }
                        }
                    }
                }
            }

            int size = keyJsonStrings.size();
            if (size == 0) {
                return "{}"; // ""

            } else {
                String includeClassType = "";
                if (classMapper.includeClassTypeInJson) { //getIncludeClassTypeInJson()
                    includeClassType = repeatedItem + "\"@class\":" + pretty + "\"" + valueType.getName() + "\",";
                }

                if (exposed != null && exposed.size() > 0) {
                    Map<String, String> map = new LinkedHashMap<>();

                    for (String key : keyJsonStrings.keySet()) {
                        if (exposed.contains(key)) {
                            map.put(key, keyJsonStrings.get(key));
                        }
                    }

                    keyJsonStrings = map;
                }

                if (keyJsonStrings.size() == 1 && this.isValueOnly()) {
                    for (Map.Entry<String, String> entry : keyJsonStrings.entrySet()) {
                        if (entry.getKey().toLowerCase().equals("value")) {
                            String value = entry.getValue();
                            String[] values = value.split(":");
                            value = null;
                            if (values.length == 1) {
                                value = values[0];
                            } else if (values.length == 2) {
                                value = values[1];
                            }
                            if (value != null && value.length() > 1) {
                                return value.substring(0, value.length() - 1);
                            }
                        }
                    }
                }

                // based on sorting requirements
                StringBuffer sb = new StringBuffer();
                if (classMapper.propertyOrders != null) {
                    for (String property : classMapper.propertyOrders) {
                        property = property.toLowerCase();
                        String jsonText = keyJsonStrings.get(property);
                        if (jsonText != null) {
                            sb.append(jsonText);
                            keyJsonStrings.remove(property);
                        } else {
                            property = fieldNames.get(property);
                            if (property != null && keyJsonStrings.containsKey(property)) {
                                sb.append(keyJsonStrings.get(property));
                                keyJsonStrings.remove(property);
                            }
                        }
                    }
                }

                List<String> properties = new ArrayList(keyJsonStrings.keySet());
                if (classMapper.orderByKeyAndProperties) {
                    Collections.sort(properties);
                }

                for (String property : properties) {
                    sb.append(keyJsonStrings.get(property));
                }

                String text = sb.toString();
                size = text.length();
                if (size == 0) {
                    return "{}";
                } else {
                    return "{" + includeClassType + text.substring(0, size - 1) + repeated + "}";
                }
            }

        } catch (IllegalArgumentException | SecurityException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
            // } catch (InvocationTargetException e) {
        }
    }

    ////////////////////////////////////////////////////////////////////////////////
    // START OF package methods
    ////////////////////////////////////////////////////////////////////////////////

    public static Object fromJsonMap(Object obj) {
        FIELD_NAMING naming = FIELD_NAMING.FIELD;
        return fromJsonMap(obj, naming);
    }

    /*
     * Rely on JSONObject to parse original json text
     * more confortable to work with map and list
     * instead of JSONObject and JSONArray
     */
    public static Object fromJsonMap(Object obj, FIELD_NAMING naming) {
        if (obj instanceof JSONArray) {
            JSONArray jobj = (JSONArray) obj; // .getJSONArray(key);

            List<Object> list = new ArrayList();
            int size = jobj.length();
            try {
                for (int i = 0; i < size; i++) {
                    list.add(fromJsonMap(jobj.get(i)));
                }
            } catch (JSONException ex) {
            }

            return list;

        } else if (obj instanceof JSONObject) {
            JSONObject jobj = (JSONObject) obj;

            // since JSONObject gives unordered map, nothing can be done about it?
            // LinkedHashMap
            Map<String, Object> map = new LinkedHashMap<String, Object>();

            Iterator<?> keys = jobj.keys();

            try {
                while (keys.hasNext()) {
                    String key = (String) keys.next();
                    String keyName = key;
                    if (naming != FIELD_NAMING.FIELD) {
                        keyName = StringUtil.formatName(key, naming);
                    }

                    if (!jobj.isNull(key)) {

                        map.put(keyName, fromJsonMap(jobj.get(key)));
                    } else {
                        map.put(keyName, null);
                    }
                }
            } catch (JSONException ex) {
            }

            return map;

        } else {
            return obj;
        }
    }

    <T> T fromJsonMap(String source, Type type) {
        try {
            Class<T> valueType = null;
            ComponentType componentType = null;
            boolean started = false;
            if (type != null) {
                if (ComponentType.class.isAssignableFrom(type.getClass())) {
                    componentType = (ComponentType) type;
                } else {
                    componentType = new ComponentType(type);
                }
                valueType = componentType.getClassType();

                startCachedComponentTypes(componentType);

                started = true;

            } else {

            }

            return fromJsonMap(source, valueType, started);

        } catch (JSONException ex) {
            //ex.printStackTrace();
        }

        return null;
    }

    <T> T fromJsonMap(String source, Class<T> valueType, boolean started) {
        return fromJsonMap(source, valueType, null, started);
    }

    private String removeComments(String source) {
        return StringUtil.applyPatterns(source, getPatterns());
    }

    // used to test data types only
    public static Object getListMapObject(String source) {
        return getListMapObject(source, FIELD_NAMING.FIELD);
    }

    public static Object getListMapObject(String source, FIELD_NAMING naming) {
        Object obj;
        if (source.startsWith("[")) {
            obj = new JSONArray(source);
        } else {
            obj = ObjectUtil.getJSONObject(source);
        }

        return fromJsonMap(obj, naming);
    }

    <T> T fromJsonMap(String source, Class<T> valueType, T object, boolean started) {
        if (source == null) {
            return null;
        }

        try {

            if (valueType == null && object != null) {
                valueType = (Class<T>) object.getClass();
            }

            source = removeComments(source);

            source = source.trim();//
            if (source.startsWith("[") && (ObjectUtil.isArrayOrCollection(valueType)
                    || (this.isMap2ListStyle() && Map.class.isAssignableFrom(valueType)))) {
                List list = null;
                //            try { // JSONArray performs better than ObjectMapper
                //               list = new ObjectMapper().readValue(source, List.class);
                //            } catch (IOException e) {
                JSONArray obj = new JSONArray(source);
                list = (List) fromJsonMap(obj);
                //            }

                if (!started) {
                    startCachedComponentTypes(valueType);
                }

                return json2Object(new FieldData(list, valueType, object, true));

            } else if (source.startsWith("{") && ObjectUtil.isMapOrObject(valueType)) {

                Map<String, Object> map = null;
                //            try {
                JSONObject obj = ObjectUtil.getJSONObject(source);
                map = (Map) fromJsonMap(obj);

                //            } catch (Exception e1) {
                //               try {
                //                  map = getJackson().readValue(source, Map.class);
                //               } catch(Exception ex) {
                //                  map = new HashMap<>();
                //               }
                //            }

                if (valueType == null) {
                    String className = (String) map.get(getJsonClassType());
                    if (className != null && className.length() > 0) {
                        try {
                            valueType = (Class<T>) Class.forName(className);
                        } catch (ClassNotFoundException e) {
                            // e.printStackTrace();
                        }
                    }
                }

                if (!started) {
                    startCachedComponentTypes(valueType);
                }

                if (valueType == null || Map.class.isAssignableFrom(valueType)) {
                    return json2Object(new FieldData(map, valueType, object, true));

                } else {
                    if (object == null) {
                        return deserialize2Object(map, valueType, null);
                    } else {
                        return deserialize2Object(new FieldData(map, valueType, object, true));
                    }
                }

            } else {
                if (!started) {
                    startCachedComponentTypes(valueType);
                }

                return json2Object(new FieldData(source, valueType, true));
            }

        } catch (JSONException ex) {
            //ex.printStackTrace();
            //throw ex;
        }

        return null;
    }

    private Class getClassType(String className) {
        if (className == null) {
            return null;
        }

        if (className.startsWith(".")) {
            String path = new OsonSecurityManager().getInitialCallerClassName();
            if (path == null) {
                return null;
            }

            int idx = path.lastIndexOf('.');
            if (idx == -1) {
                return null;
            }

            className = path.substring(0, idx) + className;
        }

        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            // e.printStackTrace();
        }

        return null;
    }

    <T> T deserialize2Object(Object value, Class<T> valueType, FieldData fieldData) {
        ClassMapper classMapper = getGlobalizedClassMapper(valueType);

        Map<String, Object> map = null;

        if (value != null && Map.class.isAssignableFrom(value.getClass())) {
            map = (Map) value;
        }

        //boolean checkResult = false;
        if (map == null) {
            if (classMapper == null) {
                return null;
            }

            map = new HashMap();
            if (value != null) {
                if (value.toString().endsWith(":")) {
                    return null;
                }

                map.put(valueType.getName(), value);
            }
            //checkResult = true;
        }

        T obj = null;

        if (valueType == Class.class) {
            obj = (T) ComponentType.forName(value.toString());
        } else {
            obj = newInstance(map, valueType);
        }

        if (obj != null) {
            if (fieldData == null) {
                fieldData = new FieldData(value, valueType, obj, true);
            } else {
                fieldData.valueToProcess = value;
                fieldData.returnType = valueType;
                fieldData.returnObj = obj;
            }

            return deserialize2Object(fieldData);

            //         T t = deserialize2Object(fieldData);
            //         if (checkResult) {
            //            String source = this.useAttribute(false).setDefaultType(JSON_INCLUDE.NON_DEFAULT).serialize(t);
            ////            source = source.replaceAll("[^a-zA-Z0-9]", "");
            ////            value = (value+"").replaceAll("[^a-zA-Z0-9]", "");
            ////            if (!source.contains(value+"")) {
            //            if (DefaultValue.isDefault(source)) {
            //               return null;
            //            }
            //         }
            //         return t;

        } else {
            return null;
        }
    }

    private <T> Object getParameterValue(Map<String, Object> map, Class<T> valueType, String parameterName,
            Class parameterType) {
        Object parameterValue = getMapValue(map, parameterName);

        Class fieldType = null;
        if (Map.class.isAssignableFrom(parameterType)) {
            // just give the whole map data to it
            // it can use as much of it as it likes
            parameterValue = map;

        } else {

            if (parameterType.isPrimitive() || Number.class.isAssignableFrom(parameterType)
                    || parameterType == String.class || parameterType == Character.class
                    || parameterType == Boolean.class || Date.class.isAssignableFrom(parameterType)
                    || parameterType.isEnum() || Enum.class.isAssignableFrom(parameterType)) {
                // do nothing

            } else {
                String toGenericString = parameterType.toGenericString();
                fieldType = ObjectUtil.getComponentType(toGenericString);

                if (fieldType == null) {
                    Field[] fields = getFields(valueType);
                    for (Field field : fields) {
                        if (field.getName().equalsIgnoreCase(parameterName)) {
                            // private java.util.List<ca.oson.json.domain.Address> ca.oson.json.domain.Person.addressList
                            toGenericString = field.toGenericString();

                            // fieldType = field.getType(); // getClass();
                            fieldType = ObjectUtil.getComponentType(toGenericString);
                            break;
                        }
                    }
                }
            }

            FieldData objectDTO = null;
            if (fieldType != null) {
                // FieldData(Object valueToProcess, Type type, boolean json2Java)
                ComponentType componentType = new ComponentType(parameterType, fieldType);
                cachedComponentTypes(componentType);

                objectDTO = new FieldData(parameterValue, parameterType, true);
                objectDTO.componentType = componentType;
            } else {
                objectDTO = new FieldData(parameterValue, parameterType, true);
            }
            objectDTO.required = true;
            parameterValue = json2Object(objectDTO);
        }

        return parameterValue;
    }

    private <T> T setSingleMapValue(T obj, Class<T> valueType, Object singleMapValue, Class singleMapValueType) {
        if (obj == null || valueType == null || singleMapValue == null || singleMapValueType == null) {
            return obj;
        }

        //check all methods
        // and invoke the right one
        // public static InetAddress getByName(String host)
        //Map<String, Method> setters = getSetters(valueType);
        for (Method method : valueType.getDeclaredMethods()) {
            if (method.getParameterCount() == 1) {
                if (ObjectUtil.isSameDataType(method.getParameterTypes()[0], singleMapValueType)) {
                    Object object = ObjectUtil.getMethodValue(obj, method, singleMapValue);

                    if (object != null && valueType.isAssignableFrom(object.getClass())) {
                        return (T) object;
                    }
                }

            }

        }

        return obj;
    }

    /*
     * create an initial object of valueType type to copy data into
     */
    <T> T newInstance(Map<String, Object> map, Class<T> valueType) {
        InstanceCreator creator = getTypeAdapter(valueType);

        if (creator != null) {
            return (T) creator.createInstance(valueType);
        }

        T obj = null;

        if (valueType != null) {
            obj = (T) getDefaultValue(valueType);
            if (obj != null) {
                return obj;
            }
        }

        if (map == null) {
            return null;
        }

        // @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = As.PROPERTY, property = "@class")
        //@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "@class")
        String JsonClassType = null;

        if (valueType != null) {
            if (getAnnotationSupport()) {
                for (Annotation annotation : valueType.getAnnotations()) {
                    if (annotation instanceof JsonTypeInfo) {
                        JsonTypeInfo jsonTypeInfo = (JsonTypeInfo) annotation;
                        JsonTypeInfo.Id use = jsonTypeInfo.use();
                        JsonTypeInfo.As as = jsonTypeInfo.include();
                        if ((use == JsonTypeInfo.Id.MINIMAL_CLASS || use == JsonTypeInfo.Id.CLASS)
                                && as == As.PROPERTY) {
                            JsonClassType = jsonTypeInfo.property();
                        }
                    }
                }
            }
        }
        if (JsonClassType == null) {
            JsonClassType = getJsonClassType();
        }

        String className = null;
        if (map.containsKey(JsonClassType)) {
            className = map.get(JsonClassType).toString();
        }

        Class<T> classType = getClassType(className);

        //  && (valueType == null || valueType.isAssignableFrom(classType) || Map.class.isAssignableFrom(valueType))
        if (classType != null) {
            valueType = classType;
        }

        if (valueType == null) {
            return (T) map; // or null, which is better?
        }

        Constructor<?>[] constructors = null;

        Class implClass = null;
        if (valueType.isInterface() || Modifier.isAbstract(valueType.getModifiers())) {
            implClass = DeSerializerUtil.implementingClass(valueType.getName());
        }

        if (implClass != null) {
            constructors = implClass.getDeclaredConstructors();
        } else {
            constructors = valueType.getDeclaredConstructors();//.getConstructors();
        }

        Object singleMapValue = null;
        Class singleMapValueType = null;
        if (map.size() == 1) {
            singleMapValue = map.get(valueType.getName());

            if (singleMapValue != null) {
                singleMapValueType = singleMapValue.getClass();

                if (singleMapValueType == String.class) {
                    singleMapValue = StringUtil.unquote(singleMapValue.toString(), isEscapeHtml());
                }

                try {
                    if (valueType == Locale.class) {
                        Constructor constructor = null;
                        String[] parts = ((String) singleMapValue).split("_");
                        if (parts.length == 1) {
                            constructor = valueType.getConstructor(String.class);
                            constructor.setAccessible(true);
                            obj = (T) constructor.newInstance(singleMapValue);
                        } else if (parts.length == 2) {
                            constructor = valueType.getConstructor(String.class, String.class);
                            constructor.setAccessible(true);
                            obj = (T) constructor.newInstance(parts);
                        } else if (parts.length == 3) {
                            constructor = valueType.getConstructor(String.class, String.class, String.class);
                            constructor.setAccessible(true);
                            obj = (T) constructor.newInstance(parts);
                        }

                        if (obj != null) {
                            return obj;
                        }
                    }
                } catch (Exception e) {
                }

                Map<Class, Constructor> cmaps = new HashMap<>();
                for (Constructor constructor : constructors) {
                    //Class[] parameterTypes = constructor.getParameterTypes();

                    int parameterCount = constructor.getParameterCount();
                    if (parameterCount == 1) {

                        Class[] types = constructor.getParameterTypes();

                        cmaps.put(types[0], constructor);
                    }
                }

                if (cmaps.size() > 0) {
                    Constructor constructor = null;

                    if ((cmaps.containsKey(Boolean.class) || cmaps.containsKey(boolean.class))
                            && BooleanUtil.isBoolean(singleMapValue.toString())) {
                        constructor = cmaps.get(Boolean.class);
                        if (constructor == null) {
                            constructor = cmaps.get(boolean.class);
                        }
                        if (constructor != null) {
                            try {
                                constructor.setAccessible(true);
                                obj = (T) constructor
                                        .newInstance(BooleanUtil.string2Boolean(singleMapValue.toString()));

                                if (obj != null) {
                                    return obj;
                                }
                            } catch (Exception e) {
                            }
                        }

                    } else if (StringUtil.isNumeric(singleMapValue.toString())) {

                        Class[] classes = new Class[] { int.class, Integer.class, long.class, Long.class,
                                double.class, Double.class, Byte.class, byte.class, Short.class, short.class,
                                Float.class, float.class, BigDecimal.class, BigInteger.class, AtomicInteger.class,
                                AtomicLong.class, Number.class };

                        for (Class cls : classes) {
                            constructor = cmaps.get(cls);

                            if (constructor != null) {
                                try {
                                    obj = (T) constructor.newInstance(NumberUtil.getNumber(singleMapValue, cls));

                                    if (obj != null) {
                                        return obj;
                                    }
                                } catch (Exception e) {
                                }
                            }
                        }

                    } else if (StringUtil.isArrayOrList(singleMapValue.toString())
                            || singleMapValue.getClass().isArray()
                            || Collection.class.isAssignableFrom(singleMapValue.getClass())) {
                        for (Entry<Class, Constructor> entry : cmaps.entrySet()) {
                            Class cls = entry.getKey();
                            constructor = entry.getValue();

                            if (cls.isArray() || Collection.class.isAssignableFrom(cls)) {
                                Object listObject = null;
                                if (singleMapValue instanceof String) {
                                    JSONArray objArray = new JSONArray(singleMapValue.toString());
                                    listObject = (List) fromJsonMap(objArray);
                                } else {
                                    listObject = singleMapValue;
                                }

                                FieldData objectDTO = new FieldData(listObject, cls, true);
                                listObject = json2Object(objectDTO);
                                if (listObject != null) {
                                    try {
                                        obj = (T) constructor.newInstance(listObject);
                                        if (obj != null) {
                                            return obj;
                                        }
                                    } catch (Exception e) {
                                    }
                                }
                            }

                        }

                    }

                    for (Entry<Class, Constructor> entry : cmaps.entrySet()) {
                        Class cls = entry.getKey();
                        constructor = entry.getValue();
                        try {
                            obj = (T) constructor.newInstance(singleMapValue);
                            if (obj != null) {
                                return obj;
                            }
                        } catch (Exception e) {
                        }
                    }

                }

            }
        }

        if (implClass != null) {
            valueType = implClass;
        }

        try {
            obj = valueType.newInstance();

            if (obj != null) {
                return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
            }

        } catch (InstantiationException | IllegalAccessException e) {
            //e.printStackTrace();
        }

        ///*
        for (Constructor constructor : constructors) {
            //Class[] parameterTypes = constructor.getParameterTypes();

            int parameterCount = constructor.getParameterCount();
            if (parameterCount > 0) {
                constructor.setAccessible(true);

                Annotation[] annotations = constructor.getDeclaredAnnotations(); // getAnnotations();

                for (Annotation annotation : annotations) {
                    boolean isJsonCreator = false;
                    if (annotation instanceof JsonCreator) {
                        isJsonCreator = true;
                    } else if (annotation instanceof ca.oson.json.annotation.FieldMapper) {
                        ca.oson.json.annotation.FieldMapper fieldMapper = (ca.oson.json.annotation.FieldMapper) annotation;

                        if (fieldMapper.jsonCreator() == BOOLEAN.TRUE) {
                            isJsonCreator = true;
                        }
                    }

                    if (isJsonCreator) {
                        Parameter[] parameters = constructor.getParameters();
                        String[] parameterNames = ObjectUtil.getParameterNames(parameters);

                        //parameterCount = parameters.length;
                        Object[] parameterValues = new Object[parameterCount];
                        int i = 0;
                        for (String parameterName : parameterNames) {
                            parameterValues[i] = getParameterValue(map, valueType, parameterName,
                                    parameters[i].getType());
                            i++;
                        }

                        try {
                            obj = (T) constructor.newInstance(parameterValues);

                            if (obj != null) {
                                return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
                            }

                        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                                | InvocationTargetException e) {
                            //e.printStackTrace();
                        }
                    }
                }

            } else {
                try {
                    constructor.setAccessible(true);
                    obj = (T) constructor.newInstance();

                    if (obj != null) {
                        return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
                    }

                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                        | InvocationTargetException e) {
                    //e.printStackTrace();
                }

            }
        }
        //*/

        // try again
        for (Constructor constructor : constructors) {
            int parameterCount = constructor.getParameterCount();
            if (parameterCount > 0) {
                constructor.setAccessible(true);

                try {
                    List<String> parameterNames = ObjectUtil.getParameterNames(constructor);

                    if (parameterNames != null && parameterNames.size() > 0) {
                        Class[] parameterTypes = constructor.getParameterTypes();

                        int length = parameterTypes.length;
                        if (length == parameterNames.size()) {
                            Object[] parameterValues = new Object[length];
                            Object parameterValue;
                            for (int i = 0; i < length; i++) {
                                parameterValues[i] = getParameterValue(map, valueType, parameterNames.get(i),
                                        parameterTypes[i]);
                            }

                            try {
                                obj = (T) constructor.newInstance(parameterValues);
                                if (obj != null) {
                                    return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
                                }

                            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                                    | InvocationTargetException e) {
                                //e.printStackTrace();
                            }

                        }
                    }

                } catch (IOException e1) {
                    // e1.printStackTrace();
                }
            }
        }

        // try more
        for (Constructor constructor : constructors) {
            int parameterCount = constructor.getParameterCount();
            if (parameterCount > 0) {
                constructor.setAccessible(true);

                Class[] parameterTypes = constructor.getParameterTypes();
                List<String> parameterNames;
                try {
                    parameterNames = ObjectUtil.getParameterNames(constructor);

                    if (parameterNames != null) {
                        int length = parameterTypes.length;

                        if (length > parameterNames.size()) {
                            length = parameterNames.size();
                        }

                        Object[] parameterValues = new Object[length];
                        for (int i = 0; i < length; i++) {
                            parameterValues[i] = getParameterValue(map, valueType, parameterNames.get(i),
                                    parameterTypes[i]);
                        }

                        obj = (T) constructor.newInstance(parameterValues);
                        if (obj != null) {
                            return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
                        }
                    }

                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                        | InvocationTargetException | IOException e) {
                    //e.printStackTrace();
                }
            }
        }

        // try more
        try {
            Method[] methods = valueType.getMethods(); // .getMethod("getInstance", null);

            List<Method> methodList = new ArrayList<>();

            if (methods != null) {
                for (Method method : methods) {
                    String methodName = method.getName();

                    if (methodName.equals("getInstance") || methodName.equals("newInstance")
                            || methodName.equals("createInstance") || methodName.equals("factory")) {
                        Class returnType = method.getReturnType();

                        if (valueType.isAssignableFrom(returnType) && Modifier.isStatic(method.getModifiers())) {
                            int parameterCount = method.getParameterCount();
                            if (parameterCount == 0) {
                                try {
                                    obj = ObjectUtil.getMethodValue(null, method);
                                    if (obj != null) {
                                        return setSingleMapValue(obj, valueType, singleMapValue,
                                                singleMapValueType);
                                    }

                                } catch (IllegalArgumentException e) {
                                    // TODO Auto-generated catch block
                                    //e.printStackTrace();
                                }

                            } else {
                                methodList.add(method);
                            }

                        }
                    }
                }

                for (Method method : methodList) {
                    try {
                        int parameterCount = method.getParameterCount();
                        Object[] parameterValues = new Object[parameterCount];
                        Object parameterValue;
                        int i = 0;
                        Class[] parameterTypes = method.getParameterTypes();

                        String[] parameterNames = ObjectUtil.getParameterNames(method);

                        if (parameterCount == 1 && valueType != null && singleMapValue != null
                                && singleMapValueType != null) {

                            if (ObjectUtil.isSameDataType(parameterTypes[0], singleMapValueType)) {
                                try {
                                    obj = ObjectUtil.getMethodValue(null, method, singleMapValue);
                                    if (obj != null) {
                                        return obj;
                                    }

                                } catch (IllegalArgumentException ex) {
                                    //ex.printStackTrace();
                                }

                            }

                        } else if (parameterNames != null && parameterNames.length == parameterCount) {
                            for (String parameterName : ObjectUtil.getParameterNames(method)) {
                                parameterValues[i] = getParameterValue(map, valueType, parameterName,
                                        parameterTypes[i]);
                                i++;
                            }

                        } else {
                            // try annotation
                            Parameter[] parameters = method.getParameters();
                            parameterNames = ObjectUtil.getParameterNames(parameters);
                            parameterCount = parameters.length;
                            parameterValues = new Object[parameterCount];
                            i = 0;
                            for (String parameterName : parameterNames) {
                                parameterValues[i] = getParameterValue(map, valueType, parameterName,
                                        parameterTypes[i]);
                                i++;
                            }
                        }

                        obj = ObjectUtil.getMethodValue(null, method, parameterValues);

                        if (obj != null) {
                            return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
                        }

                    } catch (IOException | IllegalArgumentException e) {
                        //e.printStackTrace();
                    }
                }

            }

        } catch (SecurityException e) {
            // e.printStackTrace();
        }

        // try all static methods, if the return type is correct, get it as the final object
        Method[] methods = valueType.getDeclaredMethods();
        for (Method method : methods) {
            if (Modifier.isStatic(method.getModifiers())) {
                Class returnType = method.getReturnType();

                if (valueType.isAssignableFrom(returnType)) {
                    try {
                        Object[] parameterValues = null;

                        int parameterCount = method.getParameterCount();
                        if (parameterCount > 0) {
                            if (parameterCount == 1 && map.size() == 1 && singleMapValue != null
                                    && singleMapValueType != null) {
                                if (ObjectUtil.isSameDataType(method.getParameterTypes()[0], singleMapValueType)) {
                                    obj = ObjectUtil.getMethodValue(null, method, singleMapValueType);
                                    if (obj != null) {
                                        return obj;
                                    }
                                }
                            }

                            parameterValues = new Object[parameterCount];
                            Object parameterValue;
                            int i = 0;
                            Class[] parameterTypes = method.getParameterTypes();

                            String[] parameterNames = ObjectUtil.getParameterNames(method);
                            if (parameterNames != null && parameterNames.length == parameterCount) {
                                for (String parameterName : ObjectUtil.getParameterNames(method)) {
                                    parameterValues[i] = getParameterValue(map, valueType, parameterName,
                                            parameterTypes[i]);
                                    i++;
                                }

                            } else {
                                // try annotation
                                Parameter[] parameters = method.getParameters();
                                parameterNames = ObjectUtil.getParameterNames(parameters);
                                parameterCount = parameters.length;
                                parameterValues = new Object[parameterCount];
                                i = 0;
                                for (String parameterName : parameterNames) {
                                    parameterValues[i] = getParameterValue(map, valueType, parameterName,
                                            parameterTypes[i]);
                                    i++;
                                }
                            }
                        }

                        obj = ObjectUtil.getMethodValue(obj, method, parameterValues);
                        if (obj != null) {
                            return setSingleMapValue(obj, valueType, singleMapValue, singleMapValueType);
                        }

                    } catch (IOException | IllegalArgumentException e) {
                        //e.printStackTrace();
                    }

                }
            }

        }

        return null;
    }

    private <T> void setNull(Field f, T obj) {
        if (f == null || obj == null) {
            return;
        }
        try {
            f.set(obj, null);
        } catch (Exception ex) {
        }
    }

    /*
     * string to object, deserialize, set method should be used
     *
     * It involves 10 steps to apply processing rules:
     * 1. Create a blank class mapper instance and Globalize it;
     * 2. if it is a field in previous execution, and set to inherit from previous mapping, combine with previous mapping;
     * 3. Apply annotations from other sources;
     * 4. Apply annotations from Oson;
     * 5. Apply Java configuration for this particular class;
     * 6. Create a blank field mapper instance;
     * 7. get the global class mapper for this field
     * 8. Classify this field mapper with returnType class mapper
     * 9. Classify this field mapper;
     * 10. Apply annotations from other sources;
     * 11. Apply annotations from Oson;
     * 12. Apply Java configuration for this particular field.
     */
    <T> T deserialize2Object(FieldData objectDTO) {
        Object valueToProcess = objectDTO.valueToProcess;
        Map<String, Object> map = null;

        if (valueToProcess != null && Map.class.isAssignableFrom(valueToProcess.getClass())) {
            map = (Map) valueToProcess;
        } else {
            map = new HashMap<>();
        }

        Class<T> valueType = objectDTO.returnType;
        T obj = (T) objectDTO.returnObj;

        Set<String> nameKeys = new HashSet(map.keySet());

        if (valueType == null) {
            valueType = (Class<T>) obj.getClass();
        }

        // first build up the class-level processing rules

        ClassMapper classMapper = objectDTO.classMapper;
        //if (classMapper == null) {
        // 1. Create a blank class mapper instance
        classMapper = new ClassMapper(valueType);

        // 2. Globalize it
        classMapper = globalize(classMapper);
        objectDTO.classMapper = classMapper;
        //}

        if (objectDTO.fieldMapper != null && isInheritMapping()) {
            classMapper = overwriteBy(classMapper, objectDTO.fieldMapper);
        }

        objectDTO.incrLevel();

        try {
            boolean annotationSupport = getAnnotationSupport();
            Annotation[] annotations = null;

            if (annotationSupport) {
                ca.oson.json.annotation.ClassMapper classMapperAnnotation = null;

                // 3. Apply annotations from other sources
                annotations = valueType.getAnnotations();
                for (Annotation annotation : annotations) {
                    if (ignoreClass(annotation)) {
                        return null;
                    }

                    switch (annotation.annotationType().getName()) {
                    case "ca.oson.json.annotation.ClassMapper":
                        classMapperAnnotation = (ca.oson.json.annotation.ClassMapper) annotation;
                        if (!(classMapperAnnotation.serialize() == BOOLEAN.BOTH
                                || classMapperAnnotation.serialize() == BOOLEAN.FALSE)) {
                            classMapperAnnotation = null;
                        }
                        break;

                    case "ca.oson.json.annotation.ClassMappers":
                        ca.oson.json.annotation.ClassMappers classMapperAnnotations = (ca.oson.json.annotation.ClassMappers) annotation;
                        for (ca.oson.json.annotation.ClassMapper ann : classMapperAnnotations.value()) {
                            if (ann.serialize() == BOOLEAN.BOTH || ann.serialize() == BOOLEAN.FALSE) {
                                classMapperAnnotation = ann;
                                // break;
                            }
                        }
                        break;

                    case "com.google.gson.annotations.Since":
                        Since since = (Since) annotation;
                        classMapper.since = since.value();
                        break;

                    case "com.google.gson.annotations.Until":
                        Until until = (Until) annotation;
                        classMapper.until = until.value();
                        break;

                    case "com.fasterxml.jackson.annotation.JsonIgnoreProperties":
                        JsonIgnoreProperties jsonIgnoreProperties = (JsonIgnoreProperties) annotation;
                        String[] jsonnames = jsonIgnoreProperties.value();
                        if (jsonnames != null && jsonnames.length > 0) {
                            if (classMapper.jsonIgnoreProperties == null) {
                                classMapper.jsonIgnoreProperties = new HashSet();
                            }

                            classMapper.jsonIgnoreProperties.addAll(Arrays.asList(jsonnames));
                        }
                        break;

                    case "org.codehaus.jackson.annotate.JsonIgnoreProperties":
                        org.codehaus.jackson.annotate.JsonIgnoreProperties jsonIgnoreProperties2 = (org.codehaus.jackson.annotate.JsonIgnoreProperties) annotation;
                        String[] jsonnames2 = jsonIgnoreProperties2.value();
                        if (jsonnames2 != null && jsonnames2.length > 0) {
                            if (classMapper.jsonIgnoreProperties == null) {
                                classMapper.jsonIgnoreProperties = new HashSet();
                            }

                            classMapper.jsonIgnoreProperties.addAll(Arrays.asList(jsonnames2));
                        }
                        break;

                    case "com.fasterxml.jackson.annotation.JsonPropertyOrder":
                        // first come first serve
                        if (classMapper.propertyOrders == null) {
                            classMapper.propertyOrders = ((JsonPropertyOrder) annotation).value();
                        }
                        break;

                    case "org.codehaus.jackson.annotate.JsonPropertyOrder":
                        // first come first serve
                        if (classMapper.propertyOrders == null) {
                            classMapper.propertyOrders = ((org.codehaus.jackson.annotate.JsonPropertyOrder) annotation)
                                    .value();
                        }
                        break;

                    case "com.fasterxml.jackson.annotation.JsonInclude":
                        if (classMapper.defaultType == JSON_INCLUDE.NONE) {
                            JsonInclude jsonInclude = (JsonInclude) annotation;
                            switch (jsonInclude.content()) {
                            case ALWAYS:
                                classMapper.defaultType = JSON_INCLUDE.ALWAYS;
                                break;
                            case NON_NULL:
                                classMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                break;
                            case NON_ABSENT:
                                classMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                break;
                            case NON_EMPTY:
                                classMapper.defaultType = JSON_INCLUDE.NON_EMPTY;
                                break;
                            case NON_DEFAULT:
                                classMapper.defaultType = JSON_INCLUDE.NON_DEFAULT;
                                break;
                            case USE_DEFAULTS:
                                classMapper.defaultType = JSON_INCLUDE.DEFAULT;
                                break;
                            }
                        }
                        break;

                    case "com.fasterxml.jackson.annotation.JsonAutoDetect":
                        JsonAutoDetect jsonAutoDetect = (JsonAutoDetect) annotation;
                        if (jsonAutoDetect.fieldVisibility() == Visibility.NONE) {
                            classMapper.useField = false;
                        } else if (jsonAutoDetect.fieldVisibility() != Visibility.DEFAULT) {
                            classMapper.useField = true;
                        }
                        if (jsonAutoDetect.setterVisibility() == Visibility.NONE) {
                            classMapper.useAttribute = false;
                        } else if (jsonAutoDetect.setterVisibility() != Visibility.DEFAULT) {
                            classMapper.useAttribute = true;
                        }

                        break;

                    case "org.codehaus.jackson.annotate.JsonAutoDetect":
                        org.codehaus.jackson.annotate.JsonAutoDetect jsonAutoDetect2 = (org.codehaus.jackson.annotate.JsonAutoDetect) annotation;
                        if (jsonAutoDetect2
                                .fieldVisibility() == org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE) {
                            classMapper.useField = false;
                        }
                        if (jsonAutoDetect2
                                .getterVisibility() == org.codehaus.jackson.annotate.JsonAutoDetect.Visibility.NONE) {
                            classMapper.useAttribute = false;
                        }

                        break;

                    case "org.junit.Ignore":
                        classMapper.ignore = true;
                        break;
                    }
                }

                // 4. Apply annotations from Oson
                if (classMapperAnnotation != null) {
                    classMapper = overwriteBy(classMapper, classMapperAnnotation);
                }
            }

            // 5. Apply Java configuration for this particular class
            ClassMapper javaClassMapper = getClassMapper(valueType);
            if (javaClassMapper != null) {
                classMapper = overwriteBy(classMapper, javaClassMapper);
            }

            // now processing at the class level

            if (classMapper.ignore()) {
                return null;
            }

            if (classMapper.since != null && classMapper.since > getVersion()) {
                return null;
            } else if (classMapper.until != null && classMapper.until <= getVersion()) {
                return null;
            }

            Function function = classMapper.deserializer; // = getDeserializer(valueType);
            if (function == null) {
                function = DeSerializerUtil.getDeserializer(valueType.getName());
            }
            if (function != null) {
                try {
                    Object returnedValue = null;
                    if (function instanceof Json2DataMapperFunction) {
                        DataMapper classData = new DataMapper(valueToProcess, valueType, obj, classMapper,
                                objectDTO.level, getPrettyIndentation());
                        Json2DataMapperFunction f = (Json2DataMapperFunction) function;

                        return (T) f.apply(classData);

                    } else if (function instanceof Json2FieldDataFunction) {
                        Json2FieldDataFunction f = (Json2FieldDataFunction) function;
                        FieldData fieldData = objectDTO.clone();

                        returnedValue = f.apply(fieldData);

                    } else {
                        returnedValue = function.apply(obj);
                    }

                    if (returnedValue instanceof Optional) {
                        Optional opt = (Optional) returnedValue;
                        returnedValue = opt.orElse(null);
                    }

                    if (returnedValue == null) {
                        return null;
                    } else if (valueType.isAssignableFrom(returnedValue.getClass())) {
                        return (T) returnedValue;
                    } else {
                        // not the correct returned object type, do nothing
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            Map<String, Method> getters = getGetters(valueType);
            Map<String, Method> setters = getSetters(valueType);
            Map<String, Method> otherMethods = getOtherMethods(valueType);
            Set<String> processedNameSet = new HashSet<>();

            Method jsonAnySetterMethod = null;

            Field[] fields = getFields(valueType); // getFields(obj);

            FIELD_NAMING format = getFieldNaming();

            // @Expose
            boolean exposed = false;
            if (isUseGsonExpose()) {
                // check if @exposed is used any where
                if (valueType.isAnnotationPresent(com.google.gson.annotations.Expose.class)) {
                    exposed = true;
                }
                if (!exposed) {
                    for (Field f : fields) {
                        if (f.isAnnotationPresent(com.google.gson.annotations.Expose.class)) {
                            exposed = true;
                            break;
                        }
                    }
                }
            }

            for (Field f : fields) {
                String name = f.getName();
                String fieldName = name;
                String lcfieldName = name.toLowerCase();

                Class<?> returnType = f.getType(); // value.getClass();

                if (Modifier.isFinal(f.getModifiers()) && Modifier.isStatic(f.getModifiers())) {
                    setters.remove(lcfieldName);
                    nameKeys.remove(name);
                    continue;
                }

                f.setAccessible(true);

                // getter and setter methods

                Method getter = null;
                Method setter = null;
                if (getters != null) {
                    getter = getters.get(lcfieldName);
                }
                if (setters != null) {
                    setter = setters.get(lcfieldName);
                }

                if (ignoreModifiers(f.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                    if (setter != null) {
                        if (ignoreModifiers(setter.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                            setters.remove(lcfieldName);
                            nameKeys.remove(name);
                            continue;
                        }

                    } else {
                        continue;
                    }
                }

                // 6. Create a blank field mapper instance
                // using valueType of enclosing obj
                FieldMapper fieldMapper = new FieldMapper(name, name, valueType);

                // 7. get the class mapper of returnType
                ClassMapper fieldClassMapper = getClassMapper(returnType);

                // 8. Classify this field mapper with returnType
                fieldMapper = classifyFieldMapper(fieldMapper, fieldClassMapper);

                // 9. Classify this field mapper with enclosing class type
                fieldMapper = classifyFieldMapper(fieldMapper, classMapper);

                FieldMapper javaFieldMapper = getFieldMapper(name, null, valueType);

                boolean ignored = false;

                if (setter != null) {
                    setter.setAccessible(true);
                }

                Set<String> names = new HashSet<>();

                if (annotationSupport) {
                    annotations = f.getAnnotations();

                    if (setter != null
                            && ((javaFieldMapper == null || javaFieldMapper.useAttribute == null)
                                    && (fieldMapper.useAttribute == null || fieldMapper.useAttribute))
                            || (javaFieldMapper != null && javaFieldMapper.isDeserializing()
                                    && javaFieldMapper.useAttribute != null && javaFieldMapper.useAttribute)) {
                        annotations = Stream
                                .concat(Arrays.stream(annotations), Arrays.stream(setter.getDeclaredAnnotations()))
                                .toArray(Annotation[]::new);
                    }

                    // no annotations, then try get method
                    if ((annotations == null || annotations.length == 0) && getter != null) {
                        annotations = getter.getDeclaredAnnotations();
                    }

                    ca.oson.json.annotation.FieldMapper fieldMapperAnnotation = null;

                    boolean exposexists = false;
                    for (Annotation annotation : annotations) {
                        if (ignoreField(annotation, classMapper.ignoreFieldsWithAnnotations)) {
                            ignored = true;
                            break;

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMapper) {
                            fieldMapperAnnotation = (ca.oson.json.annotation.FieldMapper) annotation;
                            if (!(fieldMapperAnnotation.serialize() == BOOLEAN.BOTH
                                    || fieldMapperAnnotation.serialize() == BOOLEAN.FALSE)) {
                                fieldMapperAnnotation = null;
                            }

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMappers) {
                            ca.oson.json.annotation.FieldMappers fieldMapperAnnotations = (ca.oson.json.annotation.FieldMappers) annotation;
                            for (ca.oson.json.annotation.FieldMapper ann : fieldMapperAnnotations.value()) {
                                if (ann.serialize() == BOOLEAN.BOTH || ann.serialize() == BOOLEAN.FALSE) {
                                    fieldMapperAnnotation = ann;
                                    //break; to enable the last one wins
                                }
                            }

                        } else {

                            switch (annotation.annotationType().getName()) {

                            case "com.fasterxml.jackson.annotation.JsonAnySetter":
                            case "org.codehaus.jackson.annotate.JsonAnySetter":
                                fieldMapper.jsonAnySetter = true;
                                break;

                            case "javax.persistence.Transient":
                                fieldMapper.ignore = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnore":
                            case "org.codehaus.jackson.annotate.JsonIgnore":
                                fieldMapper.ignore = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnoreProperties":
                                JsonIgnoreProperties jsonIgnoreProperties = (JsonIgnoreProperties) annotation;
                                if (!jsonIgnoreProperties.allowSetters()) {
                                    fieldMapper.ignore = true;
                                } else {
                                    fieldMapper.ignore = false;
                                    classMapper.jsonIgnoreProperties.remove(name);
                                }
                                break;

                            case "com.google.gson.annotations.Expose":
                                Expose expose = (Expose) annotation;
                                if (!expose.deserialize()) {
                                    fieldMapper.ignore = true;
                                }
                                exposexists = true;
                                break;

                            case "com.google.gson.annotations.Since":
                                Since since = (Since) annotation;
                                fieldMapper.since = since.value();
                                break;

                            case "com.google.gson.annotations.Until":
                                Until until = (Until) annotation;
                                fieldMapper.until = until.value();
                                break;

                            case "com.google.gson.annotations.SerializedName":
                                SerializedName serializedName = (SerializedName) annotation;
                                String[] alternates = serializedName.alternate();

                                if (alternates != null && alternates.length > 0) {
                                    for (String alternate : alternates) {
                                        names.add(alternate);
                                    }
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonInclude":
                                if (fieldMapper.defaultType == JSON_INCLUDE.NONE) {
                                    JsonInclude jsonInclude = (JsonInclude) annotation;

                                    switch (jsonInclude.content()) {
                                    case ALWAYS:
                                        fieldMapper.defaultType = JSON_INCLUDE.ALWAYS;
                                        break;
                                    case NON_NULL:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_ABSENT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_EMPTY:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_EMPTY;
                                        break;
                                    case NON_DEFAULT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_DEFAULT;
                                        break;
                                    case USE_DEFAULTS:
                                        fieldMapper.defaultType = JSON_INCLUDE.DEFAULT;
                                        break;
                                    }
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonRawValue":
                                if (((JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "org.codehaus.jackson.annotate.JsonRawValue":
                                if (((org.codehaus.jackson.annotate.JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "org.junit.Ignore":
                                fieldMapper.ignore = true;
                                break;

                            case "javax.persistence.Enumerated":
                                fieldMapper.enumType = ((Enumerated) annotation).value();
                                break;

                            case "javax.validation.constraints.NotNull":
                                fieldMapper.required = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonProperty":
                                JsonProperty jsonProperty = (JsonProperty) annotation;
                                Access access = jsonProperty.access();
                                if (access == Access.READ_ONLY) {
                                    fieldMapper.ignore = true;
                                }

                                if (jsonProperty.required()) {
                                    fieldMapper.required = true;
                                }

                                if (fieldMapper.defaultValue == null) {
                                    fieldMapper.defaultValue = jsonProperty.defaultValue();
                                }
                                break;

                            case "javax.validation.constraints.Size":
                                Size size = (Size) annotation;
                                if (size.min() > 0) {
                                    fieldMapper.min = (long) size.min();
                                }
                                if (size.max() < Integer.MAX_VALUE) {
                                    fieldMapper.max = (long) size.max();
                                }
                                break;

                            case "javax.persistence.Column":
                                Column column = (Column) annotation;
                                if (column.length() != 255) {
                                    fieldMapper.length = column.length();
                                }
                                if (column.scale() > 0) {
                                    fieldMapper.scale = column.scale();
                                }
                                if (column.precision() > 0) {
                                    fieldMapper.precision = column.precision();
                                }

                                if (!column.nullable()) {
                                    fieldMapper.required = true;
                                }
                                break;
                            }

                            String fname = ObjectUtil.getName(annotation);
                            if (!StringUtil.isEmpty(fname)) {
                                names.add(fname);
                            }

                        }
                    }

                    if (exposed && !exposexists) {
                        fieldMapper.ignore = true;
                    }

                    // 10. Apply annotations from Oson
                    if (fieldMapperAnnotation != null) {
                        fieldMapper = overwriteBy(fieldMapper, fieldMapperAnnotation, classMapper);
                    }
                }

                if (ignored) {
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    setters.remove(lcfieldName);
                    if (exposed) {
                        setNull(f, obj);
                    }
                    continue;
                }

                // 11. Apply Java configuration for this particular field
                if (javaFieldMapper != null && javaFieldMapper.isDeserializing()) {
                    fieldMapper = overwriteBy(fieldMapper, javaFieldMapper);
                }

                if (fieldMapper.ignore != null && fieldMapper.ignore) {
                    if (setter != null) {
                        setters.remove(lcfieldName);
                    }
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    if (exposed) {
                        setNull(f, obj);
                    }
                    continue;
                }

                // in the ignored list
                if (ObjectUtil.inSet(name, classMapper.jsonIgnoreProperties)) {
                    setters.remove(lcfieldName);
                    nameKeys.remove(name);
                    continue;
                }

                if (fieldMapper.jsonAnySetter != null && fieldMapper.jsonAnySetter && setter != null) {
                    setters.remove(lcfieldName);
                    otherMethods.put(lcfieldName, setter);
                    continue;
                }

                if (fieldMapper.useField != null && !fieldMapper.useField) {
                    // both should not be used, just like ignore
                    if (fieldMapper.useAttribute != null && !fieldMapper.useAttribute) {
                        getters.remove(lcfieldName);
                    }
                    continue;
                }

                if (fieldMapper.since != null && fieldMapper.since > getVersion()) {
                    if (setter != null) {
                        setters.remove(lcfieldName);
                    }
                    continue;
                } else if (fieldMapper.until != null && fieldMapper.until <= getVersion()) {
                    if (setter != null) {
                        setters.remove(lcfieldName);
                    }
                    continue;
                }

                // get value for name in map
                Object value = null;
                boolean jnameFixed = false;
                String json = fieldMapper.json;
                int size = nameKeys.size();
                if (json == null) {
                    if (setter != null) {
                        setters.remove(lcfieldName);
                    }
                    continue;

                } else if (!json.equals(name)) {
                    name = json;
                    value = getMapValue(map, name, nameKeys);
                    jnameFixed = true;
                }

                if (!jnameFixed) {
                    for (String jsoname : names) {
                        if (!name.equals(jsoname) && !StringUtil.isEmpty(jsoname)) {
                            name = jsoname;
                            value = getMapValue(map, name, nameKeys);
                            if (value != null) {
                                jnameFixed = true;
                                break;
                            }
                        }
                    }
                }

                if (!jnameFixed) {
                    value = getMapValue(map, name, nameKeys);
                    jnameFixed = true;
                }

                fieldMapper.java = fieldName;
                fieldMapper.json = name;

                // either not null, or a null value exists in the value map
                if (value != null || size == nameKeys.size() + 1) {
                    Object oldValue = value;
                    FieldData fieldData = new FieldData(obj, f, value, returnType, true, fieldMapper,
                            objectDTO.level, objectDTO.set);
                    fieldData.setter = setter;
                    Class fieldType = guessComponentType(fieldData);
                    value = json2Object(fieldData);

                    if (StringUtil.isNull(value)) {
                        if (classMapper.defaultType == JSON_INCLUDE.NON_NULL
                                || classMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                                || classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                            continue;

                        }

                    } else if (StringUtil.isEmpty(value)) {
                        if (classMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                                || classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                            continue;
                        }

                    } else if (DefaultValue.isDefault(value, returnType)) {
                        if (classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                            continue;
                        }
                    }

                    try {
                        if (value == null && oldValue != null && oldValue.equals(f.get(obj) + "")) {
                            // keep original value

                        } else {
                            f.set(obj, value);
                        }

                    } catch (IllegalAccessException | IllegalArgumentException ex) {
                        //ex.printStackTrace();
                        if (setter != null) {
                            ObjectUtil.setMethodValue(obj, setter, value);
                        }
                    }
                }

                setters.remove(lcfieldName);
                nameKeys.remove(name);
            }

            for (Entry<String, Method> entry : setters.entrySet()) {
                String lcfieldName = entry.getKey();
                Method setter = entry.getValue();

                setter.setAccessible(true);

                String name = setter.getName();
                if (name != null && name.length() > 3 && name.substring(0, 3).equals("set")
                        && name.substring(3).equalsIgnoreCase(lcfieldName)) {
                    name = StringUtil.uncapitalize(name.substring(3));
                }

                // just use field name, even it might not be a field
                String fieldName = name;

                if (ignoreModifiers(setter.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                    nameKeys.remove(name);
                    continue;
                }

                if (Modifier.isFinal(setter.getModifiers()) && Modifier.isStatic(setter.getModifiers())) {
                    nameKeys.remove(name);
                    continue;
                }

                // 6. Create a blank field mapper instance
                FieldMapper fieldMapper = new FieldMapper(name, name, valueType);

                Class returnType = null;
                Class[] types = setter.getParameterTypes();
                if (types != null && types.length > 0) {
                    returnType = types[0];
                }

                // not a proper setter
                if (returnType == null) {
                    continue;
                }

                // 7. get the class mapper of returnType
                ClassMapper fieldClassMapper = getClassMapper(returnType);

                // 8. Classify this field mapper with returnType
                fieldMapper = classifyFieldMapper(fieldMapper, fieldClassMapper);

                // 9. Classify this field mapper with enclosing class type
                fieldMapper = classifyFieldMapper(fieldMapper, classMapper);

                FieldMapper javaFieldMapper = getFieldMapper(name, null, valueType);

                boolean ignored = false;

                Method getter = getters.get(lcfieldName);

                Set<String> names = new HashSet<>();

                if (annotationSupport) {

                    annotations = setter.getDeclaredAnnotations();

                    // no annotations, then try get method
                    if ((annotations == null || annotations.length == 0) && getter != null) {
                        annotations = getter.getDeclaredAnnotations();
                    }

                    ca.oson.json.annotation.FieldMapper fieldMapperAnnotation = null;

                    for (Annotation annotation : annotations) {
                        if (ignoreField(annotation, classMapper.ignoreFieldsWithAnnotations)) {
                            ignored = true;
                            break;

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMapper) {
                            fieldMapperAnnotation = (ca.oson.json.annotation.FieldMapper) annotation;
                            if (!(fieldMapperAnnotation.serialize() == BOOLEAN.BOTH
                                    || fieldMapperAnnotation.serialize() == BOOLEAN.FALSE)) {
                                fieldMapperAnnotation = null;
                            }

                        } else if (annotation instanceof ca.oson.json.annotation.FieldMappers) {
                            ca.oson.json.annotation.FieldMappers fieldMapperAnnotations = (ca.oson.json.annotation.FieldMappers) annotation;
                            for (ca.oson.json.annotation.FieldMapper ann : fieldMapperAnnotations.value()) {
                                if (ann.serialize() == BOOLEAN.BOTH || ann.serialize() == BOOLEAN.FALSE) {
                                    fieldMapperAnnotation = ann;
                                    // break;
                                }
                            }

                        } else {
                            // to improve performance, using swith on string
                            switch (annotation.annotationType().getName()) {
                            case "com.fasterxml.jackson.annotation.JsonAnySetter":
                            case "org.codehaus.jackson.annotate.JsonAnySetter":
                                fieldMapper.jsonAnySetter = true;
                                break;

                            case "javax.persistence.Transient":
                                fieldMapper.ignore = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnore":
                            case "org.codehaus.jackson.annotate.JsonIgnore":
                                fieldMapper.ignore = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonIgnoreProperties":
                                JsonIgnoreProperties jsonIgnoreProperties = (JsonIgnoreProperties) annotation;
                                if (!jsonIgnoreProperties.allowSetters()) {
                                    fieldMapper.ignore = true;
                                } else {
                                    fieldMapper.ignore = false;
                                    classMapper.jsonIgnoreProperties.remove(name);
                                }
                                break;

                            case "com.google.gson.annotations.Expose":
                                Expose expose = (Expose) annotation;
                                if (!expose.deserialize()) {
                                    fieldMapper.ignore = true;
                                }
                                break;

                            case "com.google.gson.annotations.Since":
                                Since since = (Since) annotation;
                                fieldMapper.since = since.value();
                                break;

                            case "com.google.gson.annotations.Until":
                                Until until = (Until) annotation;
                                fieldMapper.until = until.value();
                                break;

                            case "com.fasterxml.jackson.annotation.JsonInclude":
                                if (fieldMapper.defaultType == JSON_INCLUDE.NONE) {
                                    JsonInclude jsonInclude = (JsonInclude) annotation;

                                    switch (jsonInclude.content()) {
                                    case ALWAYS:
                                        fieldMapper.defaultType = JSON_INCLUDE.ALWAYS;
                                        break;
                                    case NON_NULL:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_ABSENT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_NULL;
                                        break;
                                    case NON_EMPTY:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_EMPTY;
                                        break;
                                    case NON_DEFAULT:
                                        fieldMapper.defaultType = JSON_INCLUDE.NON_DEFAULT;
                                        break;
                                    case USE_DEFAULTS:
                                        fieldMapper.defaultType = JSON_INCLUDE.DEFAULT;
                                        break;
                                    }
                                }
                                break;

                            case "com.fasterxml.jackson.annotation.JsonRawValue":
                                if (((JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "org.codehaus.jackson.annotate.JsonRawValue":
                                if (((org.codehaus.jackson.annotate.JsonRawValue) annotation).value()) {
                                    fieldMapper.jsonRawValue = true;
                                }
                                break;

                            case "org.junit.Ignore":
                                fieldMapper.ignore = true;
                                break;

                            case "javax.persistence.Enumerated":
                                fieldMapper.enumType = ((Enumerated) annotation).value();
                                break;

                            case "javax.validation.constraints.NotNull":
                                fieldMapper.required = true;
                                break;

                            case "com.fasterxml.jackson.annotation.JsonProperty":
                                JsonProperty jsonProperty = (JsonProperty) annotation;
                                Access access = jsonProperty.access();
                                if (access == Access.READ_ONLY) {
                                    fieldMapper.ignore = true;
                                }

                                if (jsonProperty.required()) {
                                    fieldMapper.required = true;
                                }

                                if (fieldMapper.defaultValue == null) {
                                    fieldMapper.defaultValue = jsonProperty.defaultValue();
                                }
                                break;

                            case "javax.validation.constraints.Size":
                                Size size = (Size) annotation;
                                if (size.min() > 0) {
                                    fieldMapper.min = (long) size.min();
                                }
                                if (size.max() < Integer.MAX_VALUE) {
                                    fieldMapper.max = (long) size.max();
                                }
                                break;

                            case "javax.persistence.Column":
                                Column column = (Column) annotation;
                                if (column.length() != 255) {
                                    fieldMapper.length = column.length();
                                }
                                if (column.scale() > 0) {
                                    fieldMapper.scale = column.scale();
                                }
                                if (column.precision() > 0) {
                                    fieldMapper.precision = column.precision();
                                }

                                if (!column.nullable()) {
                                    fieldMapper.required = true;
                                }
                                break;
                            }

                            String fname = ObjectUtil.getName(annotation);
                            if (fname != null) {
                                names.add(fname);
                            }
                        }
                    }

                    // 10. Apply annotations from Oson
                    if (fieldMapperAnnotation != null) {
                        fieldMapper = overwriteBy(fieldMapper, fieldMapperAnnotation, classMapper);
                    }
                }

                if (ignored) {
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    continue;
                }

                // 11. Apply Java configuration for this particular field
                if (javaFieldMapper != null && javaFieldMapper.isDeserializing()) {
                    fieldMapper = overwriteBy(fieldMapper, javaFieldMapper);
                }

                if (fieldMapper.ignore != null && fieldMapper.ignore) {
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    continue;
                }

                // in the ignored list
                if (ObjectUtil.inSet(name, classMapper.jsonIgnoreProperties)) {
                    nameKeys.remove(name);
                    continue;
                }

                if (fieldMapper.useAttribute != null && !fieldMapper.useAttribute) {
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    continue;
                }

                if (fieldMapper.jsonAnySetter != null && fieldMapper.jsonAnySetter && setter != null) {
                    setters.remove(lcfieldName);
                    otherMethods.put(lcfieldName, setter);
                    continue;
                }

                if (fieldMapper.since != null && fieldMapper.since > getVersion()) {
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    continue;
                } else if (fieldMapper.until != null && fieldMapper.until <= getVersion()) {
                    nameKeys.remove(name);
                    nameKeys.remove(fieldMapper.json);
                    continue;
                }

                // get value for name in map
                Object value = null;
                boolean jnameFixed = false;
                String json = fieldMapper.json;
                if (json == null) {
                    continue;

                } else if (!json.equals(name)) {
                    name = json;
                    value = getMapValue(map, name, nameKeys);
                    jnameFixed = true;
                }

                if (!jnameFixed) {
                    for (String jsoname : names) {
                        if (!name.equals(jsoname) && !StringUtil.isEmpty(jsoname)) {
                            name = jsoname;
                            value = getMapValue(map, name, nameKeys);
                            jnameFixed = true;
                            break;
                        }
                    }
                }

                if (!jnameFixed) {
                    value = getMapValue(map, name, nameKeys);
                    jnameFixed = true;
                }

                fieldMapper.java = fieldName;
                fieldMapper.json = name;

                if (value != null) {

                    FieldData fieldData = new FieldData(obj, null, value, returnType, true, fieldMapper,
                            objectDTO.level, objectDTO.set);
                    fieldData.setter = setter;
                    Class fieldType = guessComponentType(fieldData);

                    value = json2Object(fieldData);

                    if (StringUtil.isNull(value)) {
                        if (classMapper.defaultType == JSON_INCLUDE.NON_NULL
                                || classMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                                || classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                            continue;

                        }

                    } else if (StringUtil.isEmpty(value)) {
                        if (classMapper.defaultType == JSON_INCLUDE.NON_EMPTY
                                || classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                            continue;
                        }

                    } else if (DefaultValue.isDefault(value, returnType)) {
                        if (classMapper.defaultType == JSON_INCLUDE.NON_DEFAULT) {
                            continue;
                        }
                    }

                    ObjectUtil.setMethodValue(obj, setter, value);

                    nameKeys.remove(name);
                }
            }

            if (annotationSupport) {
                //@JsonAnySetter
                if (nameKeys.size() > 0) {
                    for (Entry<String, Method> entry : otherMethods.entrySet()) {
                        Method method = entry.getValue();

                        if (ignoreModifiers(method.getModifiers(), classMapper.includeFieldsWithModifiers)) {
                            continue;
                        }

                        if (method.isAnnotationPresent(JsonAnySetter.class)) {
                            if (ignoreField(JsonAnySetter.class, classMapper.ignoreFieldsWithAnnotations)) {
                                continue;
                            }

                            jsonAnySetterMethod = method;

                        } else if (method.isAnnotationPresent(org.codehaus.jackson.annotate.JsonAnySetter.class)) {
                            if (ignoreField(org.codehaus.jackson.annotate.JsonAnySetter.class,
                                    classMapper.ignoreFieldsWithAnnotations)) {
                                continue;
                            }

                            jsonAnySetterMethod = method;

                        } else if (method.isAnnotationPresent(ca.oson.json.annotation.FieldMapper.class)) {
                            ca.oson.json.annotation.FieldMapper annotation = (ca.oson.json.annotation.FieldMapper) method
                                    .getAnnotation(ca.oson.json.annotation.FieldMapper.class);
                            if (annotation.jsonAnySetter() == BOOLEAN.TRUE) {
                                jsonAnySetterMethod = method;
                                break;
                            }
                        }
                    }
                }
            }

            if (jsonAnySetterMethod != null) {
                Parameter[] parameters = jsonAnySetterMethod.getParameters();
                if (parameters != null && parameters.length == 2) {
                    for (String name : nameKeys) {
                        Object value = map.get(name);

                        // json to java, check if this name is allowed or changed
                        String java = json2Java(name);

                        if (value != null && !StringUtil.isEmpty(java)) {
                            ObjectUtil.setMethodValue(obj, jsonAnySetterMethod, java, value);
                        }

                    }
                }
            }

            return obj;

            // | InvocationTargetException
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }

        return null;
    }

    ////////////////////////////////////////////////////////////////////////////////
    // START OF public methods
    ////////////////////////////////////////////////////////////////////////////////

    /*
     * Deserialize JSONObject object, to Map object
     */
    public <T> T deserialize(JSONObject source) {
        JSON_PROCESSOR processor = getJsonProcessor();
        if (processor == JSON_PROCESSOR.JACKSON || processor == JSON_PROCESSOR.GSON) {
            return deserialize(source.toString());
        }

        Map<String, Object> map = (Map) fromJsonMap(source);

        return json2Object(new FieldData(map, null, null, true));
    }

    public <T> T deserialize(JSONObject source, T obj) {
        JSON_PROCESSOR processor = getJsonProcessor();
        if (processor == JSON_PROCESSOR.JACKSON || processor == JSON_PROCESSOR.GSON) {
            return deserialize(source.toString(), obj);
        }

        Map<String, Object> map = (Map) fromJsonMap(source);

        return json2Object(new FieldData(map, null, obj, true));
    }

    public <T> T deserialize(JSONObject source, Class<T> valueType) {
        JSON_PROCESSOR processor = getJsonProcessor();
        if (processor == JSON_PROCESSOR.JACKSON || processor == JSON_PROCESSOR.GSON) {
            return deserialize(source.toString(), valueType);
        }

        ComponentType componentType = new ComponentType(valueType);
        startCachedComponentTypes(componentType);

        Map<String, Object> map = (Map) fromJsonMap(source);

        return json2Object(new FieldData(map, valueType, null, true));
    }

    public <T> T deserialize(JSONObject source, Type type) {
        JSON_PROCESSOR processor = getJsonProcessor();
        if (processor == JSON_PROCESSOR.JACKSON || processor == JSON_PROCESSOR.GSON) {
            return deserialize(source.toString(), type);
        }

        Map<String, Object> map = (Map) fromJsonMap(source);

        Class<T> valueType = null;
        ComponentType componentType = null;
        if (type != null) {
            if (ComponentType.class.isAssignableFrom(type.getClass())) {
                componentType = (ComponentType) type;
            } else {
                componentType = new ComponentType(type);
            }
            valueType = componentType.getClassType();

            startCachedComponentTypes(componentType);
        }

        return deserialize(source, valueType);
    }

    /*
     * string to object, deserialize, set method should be used
     */
    public <T> T deserialize(String source, T obj) {
        if (source == null || source.length() < 2 || obj == null) {
            return null;
        }
        source = source.trim();

        if (source.length() < 2 || (source.startsWith("[") && !source.endsWith("]"))
                || (source.startsWith("{") && !source.endsWith("}"))) {
            return null;
        }

        Class<T> valueType = (Class<T>) obj.getClass();
        JSON_PROCESSOR processor = getJsonProcessor();

        if (processor == JSON_PROCESSOR.JACKSON || processor == JSON_PROCESSOR.GSON) {
            return fromJson(source, valueType);
        }

        try {
            return fromJsonMap(source, valueType, obj, false);

        } catch (IllegalArgumentException e) {
            //e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    public <T> T deserialize(String source, Class<T> valueType) {
        JSON_PROCESSOR processor = getJsonProcessor();

        if (processor == JSON_PROCESSOR.JACKSON) {
            try {
                return getJackson().readValue(source, valueType);
            } catch (IOException e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }

        } else if (processor == JSON_PROCESSOR.GSON) {
            try {
                return getGson().fromJson(source, valueType);
            } catch (Exception e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }
        }

        return fromJsonMap(source, valueType);
    }

    public <T> T deserialize(String source, Type type) {
        JSON_PROCESSOR processor = getJsonProcessor();

        if (processor == JSON_PROCESSOR.JACKSON) {
            try {
                return (T) getJackson().readValue(source, new TypeReference<T>() {
                    @Override
                    public Type getType() {
                        return type;
                    }
                }); // ObjectUtil.getTypeClass(type)
            } catch (IOException e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }

        } else if (processor == JSON_PROCESSOR.GSON) {
            try {
                return getGson().fromJson(source, type);
            } catch (Exception e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }
        }

        return fromJsonMap(source, type);
    }

    public <T> String serialize(T source, Type type) {
        JSON_PROCESSOR processor = getJsonProcessor();

        // should not reach here, just for reference
        if (processor == JSON_PROCESSOR.JACKSON) {
            try {
                return getJackson().writeValueAsString(source);
            } catch (JsonProcessingException e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }
        } else if (processor == JSON_PROCESSOR.GSON) {
            try {
                return getGson().toJson(source, type);
            } catch (Exception e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }
        }

        Class<T> valueType = null;
        ComponentType componentType = null;
        if (type != null) {
            if (ComponentType.class.isAssignableFrom(type.getClass())) {
                componentType = (ComponentType) type;
            } else {
                componentType = new ComponentType(type);
            }
            valueType = componentType.getClassType();
        }

        if (valueType == null) {
            valueType = (Class<T>) source.getClass();
        }

        return object2Json(new FieldData(source, valueType, componentType, false));
    }

    public <T> String serialize(T source, Class<T> valueType) {
        JSON_PROCESSOR processor = getJsonProcessor();

        // should not reach here, just for reference
        if (processor == JSON_PROCESSOR.JACKSON) {
            try {
                return getJackson().writeValueAsString(source);
            } catch (JsonProcessingException e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }
        } else if (processor == JSON_PROCESSOR.GSON) {
            try {
                return getGson().toJson(source, valueType);
            } catch (Exception e) {
                if (getPrintErrorUseOsonInFailure()) {
                    e.printStackTrace();
                } else {
                    throw new RuntimeException(e);
                }
            }
        }

        if (valueType == null) {
            valueType = (Class<T>) source.getClass();
        }

        String value = object2Json(new FieldData(source, valueType, false));
        if (value == null) {
            return "null";
        }
        return value;
    }

    public <T> String serialize(T source) {
        if (source == null) {
            return "null";
        }

        Class<T> valueType = (Class<T>) source.getClass();

        return serialize(source, valueType);
    }

    public <T> T deserialize(String source) {
        return deserialize(source, null);
    }

    public <T> T fromJson(String source) {
        return deserialize(source);
    }

    public <T> T fromJson(String source, T obj) {
        return deserialize(source, obj);
    }

    public <T> T fromJson(String source, Class<T> valueType) {
        return deserialize(source, valueType);
    }

    public <T> T fromJson(String source, Type type) {
        return deserialize(source, type);
    }

    public <T> String toJson(T source, Type type) {
        return serialize(source, type);
    }

    public <T> String toJson(T source, Class<T> valueType) {
        return serialize(source, valueType);
    }

    public <T> String toJson(T source) {
        return serialize(source);
    }

    public <T> T readValue(String source) {
        return deserialize(source);
    }

    public <T> T readValue(String source, T obj) {
        return deserialize(source, obj);
    }

    public <T> T readValue(String source, Class<T> valueType) {
        return deserialize(source, valueType);
    }

    public <T> T readValue(String source, Type type) {
        return deserialize(source, type);
    }

    public <T> String writeValueAsString(T source, Type type) {
        return serialize(source, type);
    }

    public <T> String writeValueAsString(T source) {
        return serialize(source);
    }

    public static class OsonSecurityManager extends SecurityManager {
        public Class[] context() {
            return getClassContext();
        }

        public String getInitialCallerClassName() {
            Class[] classes = context();
            if (classes == null || classes.length == 0) {
                return null;
            }
            return classes[classes.length - 1].getName();
        }
    }

}