com.feilong.tools.jsonlib.JsonUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.tools.jsonlib.JsonUtil.java

Source

/*
 * Copyright (C) 2008 feilong
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.feilong.tools.jsonlib;

import static org.apache.commons.lang3.StringUtils.EMPTY;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.feilong.core.bean.ConvertUtil;
import com.feilong.core.lang.ArrayUtil;
import com.feilong.core.lang.ClassUtil;
import com.feilong.core.lang.ObjectUtil;
import com.feilong.core.lang.reflect.FieldUtil;
import com.feilong.tools.jsonlib.filters.ArrayContainsPropertyNamesPropertyFilter;
import com.feilong.tools.jsonlib.processor.DateJsonValueProcessor;
import com.feilong.tools.jsonlib.processor.SensitiveWordsJsonValueProcessor;
import com.feilong.tools.jsonlib.util.PropertyStrategyWrapper;

import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;

import static com.feilong.core.DatePattern.COMMON_DATE;
import static com.feilong.core.DatePattern.COMMON_DATE_AND_TIME;
import static com.feilong.core.DatePattern.COMMON_TIME;

import net.sf.ezmorph.MorpherRegistry;
import net.sf.ezmorph.object.DateMorpher;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.processors.JsonValueProcessor;
import net.sf.json.util.CycleDetectionStrategy;
import net.sf.json.util.JSONUtils;
import net.sf.json.util.PropertySetStrategy;

/**
 * json?.
 * 
 * <h3>???:</h3>
 * <blockquote>
 * <table border="1" cellspacing="0" cellpadding="4" summary="">
 * <tr style="background-color:#ccccff">
 * <th align="left">:</th>
 * <th align="left">:</th>
 * </tr>
 * <tr valign="top">
 * <td>{@link #format(Object)}</td>
 * <td>??json</td>
 * </tr>
 * <tr valign="top" style="background-color:#eeeeff">
 * <td>{@link #toJSON(Object)}</td>
 * <td>Bean?Map?????Json.</td>
 * </tr>
 * </table>
 * </blockquote>
 * 
 * <h3>json-lib format map json?//map</h3>
 * 
 * <blockquote>
 * <p>
 * see {@link net.sf.json.JSONObject#_fromMap(Map, JsonConfig)}
 * </p>
 * <ul>
 * <li>key?null</li>
 * <li>key?"null" </li>
 * </ul>
 * </blockquote>
 * 
 * 
 * <h3>??jar:</h3>
 * 
 * <blockquote>
 * 
 * <pre class="code">
 * {@code
 * <groupId>net.sf.json-lib</groupId>
 * <artifactId>json-lib</artifactId>
 * }
 * 
 * ? xml,?
 * 
 * {@code
 * <groupId>xom</groupId> 
 * <artifactId>xom</artifactId>
 * }
 * 
 * ??????XML,  xstreamXML??
 * </pre>
 * 
 * </blockquote>
 * 
 * @author <a href="http://feitianbenyue.iteye.com/">feilong</a>
 * @see net.sf.json.JSONSerializer#toJSON(Object, JsonConfig)
 * 
 * @see net.sf.json.JSONObject
 * @see net.sf.json.JSONArray
 * @see net.sf.json.JSONNull
 * @since 1.0.5
 */
//XXX @deprecated net.sf.json-lib Non-maintenance,will use Jackson instead
public final class JsonUtil {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);

    /** The Constant DEFAULT_JSON_CONFIG. */
    private static final JsonConfig DEFAULT_JSON_CONFIG;

    //***********************************************************************************
    /** The Constant SENSITIVE_WORDS_JSONVALUE_PROCESSOR. */
    private static final SensitiveWordsJsonValueProcessor SENSITIVE_WORDS_JSONVALUE_PROCESSOR = new SensitiveWordsJsonValueProcessor();

    /** The Constant SENSITIVE_WORDS_PROPERTY_NAMES. */
    private static final String[] SENSITIVE_WORDS_PROPERTY_NAMES = { "password", "key" };

    /** Don't let anyone instantiate this class. */
    private JsonUtil() {
        //AssertionError?. ?????. ???.
        //see Effective Java 2nd
        throw new AssertionError("No " + getClass().getName() + " instances for you!");
    }

    //***********************************************************************************
    /**
     * ??.
     */
    static {
        // 
        MorpherRegistry morpherRegistry = JSONUtils.getMorpherRegistry();
        // ???,?Json??
        morpherRegistry.registerMorpher(
                new DateMorpher(ConvertUtil.toArray(COMMON_DATE_AND_TIME, COMMON_TIME, COMMON_DATE)));

        DEFAULT_JSON_CONFIG = getDefaultJsonConfig();
    }

    //***************************format********************************************************

    // [start] format

    /**
     *  <code>obj</code> ??json.
     * 
     * <p>
     * ??json???
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
    {"userAddresseList":[{"address":"?1188?H109-118"},{"address":"28025?802()"}],"userAddresses":[{"address":"?1188?H109-118"},{"address":"28025?802()"}],"date":"2016-06-09 17:40:28","password":"******","id":8,"nickName":[],"age":0,"name":"feilong","money":99999999,"attrMap":null,"userInfo":{"age":10},"loves":["?",""]}
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {
        "userAddresseList":         [
            {"address": "?1188?H109-118"},
            {"address": "28025?802()"}
        ],
        "userAddresses":         [
            {"address": "?1188?H109-118"},
            {"address": "28025?802()"}
        ],
        "date": "2016-06-09 17:40:28",
        "password": "******",
        "id": 8,
        "nickName": [],
        "age": 0,
        "name": "feilong",
        "money": 99999999,
        "attrMap": null,
        "userInfo": {"age": 10},
        "loves":         [
            "?",
            ""
        ]
    }
     * 
     * </pre>
     * 
     * </blockquote>
     * 
     * <h3> <code>indent</code>:</h3>
     * 
     * <blockquote>
     * <p>
     *  toString(4,4) 
     * </p>
     * 
     * <p>
     * ?? <code>indent</code>,? {@link #format(Object, int, int)} {@link #format(Object, JsonConfig, int, int)}
     * {@link #format(Object, JsonFormatConfig, int, int)}
     * </p>
     * </blockquote>
     * 
     * @param obj
     *            
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     * @see #format(Object, JsonFormatConfig)
     */
    public static String format(Object obj) {
        return format(obj, (JsonFormatConfig) null);
    }

    /**
     * map ??,request?, map?json??.
     * 
     * <h3>?:</h3>
     * 
     * <blockquote>
     * <ul>
     * <li> inputMap? simpleMap(<span style="color:red">inputMap??</span>)</li>
     * <li>?simpleMap {@link TreeMap},?json key??</li>
     * </ul>
     * </blockquote>
     * 
     * <h3>?:</h3>
     * 
     * <blockquote>
     * <ul>
     * <li>value isPrimitiveOrWrapper, ?  simpleMap</li>
     * <li>?  {@link String#valueOf(Object)} ?simpleMap</li>
     * </ul>
     * </blockquote>.
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param inputMap
     *            the input map
     * @return  <code>inputMap</code> null, {@link StringUtils#EMPTY}<br>
     * @since 1.3.0
     */
    public static <K, V> String formatSimpleMap(Map<K, V> inputMap) {
        return null == inputMap ? EMPTY : formatSimpleMap(inputMap, (Class<?>) null);
    }

    /**
     * map ??,request?, map?json??.
     * 
     * <h3>?:</h3>
     * 
     * <blockquote>
     * <ul>
     * <li> inputMap? simpleMap(<span style="color:red">inputMap??</span>)</li>
     * <li>?simpleMap {@link TreeMap},?json key??</li>
     * </ul>
     * </blockquote>
     * 
     * <h3>?:</h3>
     * 
     * <blockquote>
     * <ul>
     * <li>valueisPrimitiveOrWrapper,?simpleMap</li>
     * <li>?{@link String#valueOf(Object)}?simpleMap</li>
     * </ul>
     * </blockquote>.
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param inputMap
     *            the input map
     * @param allowFormatClassTypes
     *            ,?,??json format
     * @return  <code>inputMap</code> null, {@link StringUtils#EMPTY}<br>
     * @since 1.3.0
     */
    public static <K, V> String formatSimpleMap(Map<K, V> inputMap, Class<?>... allowFormatClassTypes) {
        if (null == inputMap) {
            return EMPTY;
        }
        Map<K, Object> simpleMap = new TreeMap<K, Object>();
        for (Map.Entry<K, V> entry : inputMap.entrySet()) {
            V value = entry.getValue();
            simpleMap.put(entry.getKey(),
                    isAllowFormatType(value, allowFormatClassTypes) ? value : String.valueOf(value)); //? String.valueOf(value)valuenull  "null"
        }
        return format(simpleMap);
    }

    /**
     * ? ?json(<b></b>?? <code>excludes</code>), toString(4, 4) .
     * 
     * <p>
     * ,<b>?</b>?,see {@link #formatWithIncludes(Object, String...)}
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * User user = new User();
     * 
     * user.setPassword("123456");
     * user.setId(8L);
     * user.setName("feilong");
     * user.setDate(new Date());
     * user.setMoney(toBigDecimal("99999999.00"));
     * 
     * user.setLoves(toArray("?", ""));
     * user.setUserInfo(new UserInfo(10));
     * 
     * UserAddress userAddress1 = new UserAddress("?1188?H109-118");
     * UserAddress userAddress2 = new UserAddress("28025?802()");
     * 
     * user.setUserAddresses(toArray(userAddress1, userAddress2));
     * user.setUserAddresseList(toList(userAddress1, userAddress2));
     * 
     * LOGGER.debug(JsonUtil.format(USER, toArray("name", "loves", "attrMap", "userInfo", "userAddresses")));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     *  {
    "userAddresseList":         [
        {"address": "?1188?H109-118"},
        {"address": "28025?802()"}
    ],
    "date": "2016-07-17 16:04:35",
    "password": "******",
    "id": 8,
    "age": 0,
    "money": 99999999,
    "nickNames": []
    }
     * </pre>
     * 
     * </blockquote>
     *
     * @param obj
     *            
     * @param excludes
     *            ???json, excludes isNotNullOrEmpty,?setExcludes
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     * @see <a href="http://feitianbenyue.iteye.com/blog/2046877">java.lang.ClassCastException: JSON keys must be strings</a>
     */
    public static String format(Object obj, String[] excludes) {
        return format(obj, excludes, 4, 4);
    }

    /**
     * ? ?json(<b></b>?? <code>excludes</code>),(<code>indentFactor</code> <code>indent</code>) .
     * 
     * <p>
     * ,<b>?</b>?,see {@link #formatWithIncludes(Object, String...)}
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * User user = new User();
     * 
     * user.setPassword("123456");
     * user.setId(8L);
     * user.setName("feilong");
     * user.setDate(new Date());
     * user.setMoney(toBigDecimal("99999999.00"));
     * 
     * user.setLoves(toArray("?", ""));
     * user.setUserInfo(new UserInfo(10));
     * 
     * UserAddress userAddress1 = new UserAddress("?1188?H109-118");
     * UserAddress userAddress2 = new UserAddress("28025?802()");
     * 
     * user.setUserAddresses(toArray(userAddress1, userAddress2));
     * user.setUserAddresseList(toList(userAddress1, userAddress2));
     * 
     * LOGGER.debug(JsonUtil.format(USER, toArray("name", "loves", "attrMap", "userInfo", "userAddresses"), 0, 0));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {"userAddresseList":[{"address":"?1188?H109-118"},{"address":"28025?802()"}],"date":"2016-07-17 16:05:34","password":"******","id":8,"age":0,"money":99999999,"nickNames":[]}
     * </pre>
     * 
     * </blockquote>
     *
     * @param obj
     *            the obj
     * @param excludes
     *            ???json, excludes isNotNullOrEmpty,?setExcludes
     * @param indentFactor
     *            the indent factor
     * @param indent
     *            the indent
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     * @see #format(Object, JsonFormatConfig)
     * @see #buildJsonFormatConfig(String[], String[])
     */
    public static String format(Object obj, String[] excludes, Integer indentFactor, Integer indent) {
        return null == obj ? EMPTY : format(obj, buildJsonFormatConfig(excludes, null), indentFactor, indent);
    }

    /**
     * ? ?json(<b>?</b>?? <code>includes</code>).
     * 
     * <p>
     * ,<b></b>?,see {@link #format(Object, String[])}? {@link #format(Object, String[], Integer, Integer)}
     * </p>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * User user1 = new User("feilong1", 24);
     * user1.setNickNames(toArray("xin.jin", "shuai.ge"));
     * User user2 = new User("feilong2", 240);
     * user2.setNickNames(toArray("xin.jin", "shuai.ge"));
     * 
     * List{@code <User>} list = toList(user1, user2);
     * 
     * LOGGER.debug(JsonUtil.formatWithIncludes(list, "name", "age"));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    [{
            "age": 24,
            "name": "feilong1"
        },
                {
            "age": 240,
            "name": "feilong2"
        }
    ]
     * </pre>
     * 
     * </blockquote>
     *
     * @param obj
     *            the obj
     * @param includes
     *            the includes
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     * @see #format(Object, JsonFormatConfig)
     * @see #buildJsonFormatConfig(String[], String[])
     * @since 1.0.8
     */
    public static String formatWithIncludes(Object obj, final String... includes) {
        return null == obj ? EMPTY : format(obj, buildJsonFormatConfig(null, includes));
    }

    /**
     * Builds the json format config.
     *
     * @param excludes
     *            the excludes
     * @param includes
     *            the includes
     * @return the json format config
     * @since 1.6.3
     */
    private static JsonFormatConfig buildJsonFormatConfig(String[] excludes, String[] includes) {
        boolean noNeedBuild = isNullOrEmpty(excludes) && isNullOrEmpty(includes);
        return noNeedBuild ? null : new JsonFormatConfig(excludes, includes);
    }

    /**
     * ? ?json,(<code>indentFactor</code> <code>indent</code>) .
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * // >10 
     * Predicate<Integer> predicate = new ComparatorPredicate<Integer>(10, ComparatorUtils.<Integer> naturalComparator(), Criterion.LESS);
     * 
     * List<Integer> result = CollectionsUtil.select(toList(1, 5, 10, 30, 55, 88, 1, 12, 3), predicate);
     * LOGGER.debug(JsonUtil.format(result, 0, 0));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * [30,55,88,12]
     * </pre>
     * 
     * <hr>
     * 
     * <pre class="code">
     * LOGGER.debug(JsonUtil.format(result, 4, 4));// = LOGGER.debug(JsonUtil.format(result))
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * [
    30,
    55,
    88,
    12
    ]
     * </pre>
     * 
     * </blockquote>
     *
     * @param obj
     *            the obj
     * @param indentFactor
     *            the indent factor
     * @param indent
     *            the indent
     * @return the string
     * @since 1.2.2
     */
    public static String format(Object obj, int indentFactor, int indent) {
        return format(obj, (JsonConfig) null, indentFactor, indent);
    }

    /**
     * ? <code>JsonFormatConfig</code> ???json.
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * User user = new User("feilong1", 24);
     * user.setPassword("123456");
     * user.setMoney(toBigDecimal("99999999.00"));
     * 
     * Map{@code <String, JsonValueProcessor>} propertyNameAndJsonValueProcessorMap = new HashMap{@code <String, JsonValueProcessor>}();
     * propertyNameAndJsonValueProcessorMap.put("password", new SensitiveWordsJsonValueProcessor());
     * propertyNameAndJsonValueProcessorMap.put("money", new BigDecimalJsonValueProcessor());
     * 
     * JsonFormatConfig jsonFormatConfig = new JsonFormatConfig();
     * jsonFormatConfig.setPropertyNameAndJsonValueProcessorMap(propertyNameAndJsonValueProcessorMap);
     * 
     * LOGGER.debug(JsonUtil.format(user, jsonFormatConfig));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
    "userAddresseList": [],
    "userAddresses": [],
    "date": null,
    "password": "******",
    "id": 0,
    "age": 24,
    "name": "feilong1",
    "money": "99999999.00",
    "attrMap": null,
    "userInfo": {"age": 0},
    "nickNames": [],
    "loves": []
    }
     * </pre>
     * 
     * <p>
     * ,<code>password</code> {@link SensitiveWordsJsonValueProcessor} ??
     * </p>
     * 
     * </blockquote>
     *
     * @param obj
     *            the obj
     * @param jsonFormatConfig
     *            the json format config
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     * @since 1.2.2
     */
    public static String format(Object obj, JsonFormatConfig jsonFormatConfig) {
        return format(obj, jsonFormatConfig, 4, 4);
    }

    /**
     * Format.
     *
     * @param obj
     *            the obj
     * @param jsonFormatConfig
     *            the json format config
     * @param indentFactor
     *            the indent factor
     * @param indent
     *            the indent
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     * @since 1.2.2
     */
    public static String format(Object obj, JsonFormatConfig jsonFormatConfig, int indentFactor, int indent) {
        if (null == jsonFormatConfig) {
            return format(obj, (JsonConfig) null, indentFactor, indent);
        }
        JsonConfig jsonConfig = getDefaultJsonConfig();

        //value?
        Map<String, JsonValueProcessor> propertyNameAndJsonValueProcessorMap = jsonFormatConfig
                .getPropertyNameAndJsonValueProcessorMap();
        if (isNotNullOrEmpty(propertyNameAndJsonValueProcessorMap)) {
            for (Map.Entry<String, JsonValueProcessor> entry : propertyNameAndJsonValueProcessorMap.entrySet()) {
                jsonConfig.registerJsonValueProcessor(entry.getKey(), entry.getValue());
            }
        }
        //
        if (isNotNullOrEmpty(jsonFormatConfig.getExcludes())) {
            jsonConfig.setExcludes(jsonFormatConfig.getExcludes());
        }
        //?
        if (isNotNullOrEmpty(jsonFormatConfig.getIncludes())) {
            jsonConfig.setJsonPropertyFilter(
                    new ArrayContainsPropertyNamesPropertyFilter(jsonFormatConfig.getIncludes()));
        }

        return format(obj, jsonConfig, indentFactor, indent);
    }

    /**
     * ? <code>obj</code> ?field ??.
     * 
     * <h3>??:</h3>
     * 
     * <blockquote>
     * <ol>
     * <li>field  {@link SensitiveWords}, {@link SensitiveWordsJsonValueProcessor}??</li>
     * </ol>
     * </blockquote>
     * 
     * @param obj
     *            the obj
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     *         ?? field name value map {@link FieldUtil#getAllFieldNameAndValueMap(Object, String...)} 
     *         {@link #format(Object, JsonFormatConfig)},?,? {@link SensitiveWords}
     * @see FieldUtil#getAllFieldNameAndValueMap(Object, String...)
     * @see org.apache.commons.lang3.reflect.FieldUtils#getFieldsListWithAnnotation(Class, Class)
     * @since 1.5.6
     */
    public static String formatObjectFieldsNameAndValueMap(Object obj) {
        return null == obj ? EMPTY : format(FieldUtil.getAllFieldNameAndValueMap(obj), buildJsonFormatConfig(obj));
    }

    /**
     * Builds the json format config.
     *
     * @param obj
     *            the obj
     * @return the json format config
     * @since 1.6.3
     */
    private static JsonFormatConfig buildJsonFormatConfig(Object obj) {
        List<Field> fieldsListWithAnnotation = FieldUtils.getFieldsListWithAnnotation(obj.getClass(),
                SensitiveWords.class);
        if (isNotNullOrEmpty(fieldsListWithAnnotation)) {
            Map<String, JsonValueProcessor> propertyNameAndJsonValueProcessorMap = new HashMap<String, JsonValueProcessor>();
            for (Field field : fieldsListWithAnnotation) {
                propertyNameAndJsonValueProcessorMap.put(field.getName(), SENSITIVE_WORDS_JSONVALUE_PROCESSOR);
            }
            return new JsonFormatConfig(propertyNameAndJsonValueProcessorMap);
        }
        return null;
    }

    /**
     * Make a prettyprinted JSON text.
     * 
     * <p>
     * Warning: This method assumes that the data structure is acyclical.
     * </p>
     *
     * @param obj
     *            the obj
     * @param jsonConfig
     *            the json config
     * @param indentFactor
     *            The number of spaces to add to each level of indentation.
     * @param indent
     *            The indentation of the top level.
     * @return  <code>obj</code> null, {@link StringUtils#EMPTY}<br>
     *         a printable,displayable,transmittable representation of the object,<br>
     *         beginning with{ (left brace) and ending with }(right brace).
     * @since 1.0.8
     */
    private static String format(Object obj, JsonConfig jsonConfig, int indentFactor, int indent) {
        return null == obj ? EMPTY : toJSON(obj, jsonConfig).toString(indentFactor, indent);
    }

    // [end]

    // [start]toJSON

    /**
     * Bean?Map?????Json.
     *
     * @param obj
     *            the obj
     * @return the jSON
     * @see #toJSON(Object, JsonConfig)
     */
    static JSON toJSON(Object obj) {
        return toJSON(obj, null);
    }

    /**
     * Bean?Map?????Json.
     * 
     * <p>
     *  <code>null==jsonConfig</code>, {@link #DEFAULT_JSON_CONFIG}
     * </p>
     *
     * @param obj
     *            the obj
     * @param jsonConfig
     *            the json config
     * @return the jSON
     * @see net.sf.json.JSONArray#fromObject(Object, JsonConfig)
     * @see net.sf.json.JSONObject#fromObject(Object, JsonConfig)
     * @see net.sf.json.util.JSONUtils#isArray(Object)
     * @see java.lang.Class#isEnum()
     * @see net.sf.json.JsonConfig#registerJsonValueProcessor(Class, JsonValueProcessor)
     * @see org.apache.commons.collections4.IteratorUtils#toList(Iterator)
     * @see org.apache.commons.collections4.IteratorUtils#toList(Iterator, int)
     * @see net.sf.json.JSONSerializer#toJSON(Object)
     */
    static JSON toJSON(Object obj, JsonConfig jsonConfig) {
        JsonConfig useJsonConfig = ObjectUtils.defaultIfNull(jsonConfig, DEFAULT_JSON_CONFIG);
        registerDefaultJsonValueProcessor(useJsonConfig);

        if (isNeedConvertToJSONArray(obj)) {
            Object arrayJsonObject = obj instanceof Iterator ? IteratorUtils.toList((Iterator<?>) obj) : obj;
            return toJSONArray(arrayJsonObject, useJsonConfig);
        }
        return toJSONObject(obj, useJsonConfig);
    }

    /**
     * ??? JSONArray.
     *
     * @param obj
     *            the obj
     * @return true, if is array
     * @see net.sf.json.JSONArray#_fromJSONTokener(net.sf.json.util.JSONTokener, JsonConfig)
     * @since 1.7.2
     */
    private static boolean isNeedConvertToJSONArray(Object obj) {
        if (obj instanceof String) {
            String str = (String) obj;
            if (str.startsWith("[") && str.endsWith("]")) {// [] ? 
                return true;
            }
        }
        return JSONUtils.isArray(obj) || //obj.getClass().isArray() || obj instanceof Collection || obj instanceof Object[]
                obj instanceof Enum || // obj.getClass().isEnum() null// object'isanEnum.UseJSONArrayinstead
                obj instanceof Iterator;
    }

    // [end]

    /**
     * ?.
     *
     * @param jsonConfig
     *            the json config
     * @since 1.5.3
     */
    private static void registerDefaultJsonValueProcessor(JsonConfig jsonConfig) {
        for (String propertyName : SENSITIVE_WORDS_PROPERTY_NAMES) {
            jsonConfig.registerJsonValueProcessor(propertyName, SENSITIVE_WORDS_JSONVALUE_PROCESSOR);
        }
    }

    //********************************************************************************************

    /**
     * To json array.
     *
     * @param json
     *            the json
     * @return the JSON array
     * @see net.sf.json.JSONArray#fromObject(Object)
     * @since 1.4.0
     */
    private static JSONArray toJSONArray(String json) {
        return toJSONArray(json, new JsonConfig());
    }

    /**
     * To json array.
     *
     * @param obj
     *            Accepts JSON formatted strings, arrays, Collections and Enums.
     * @param useJsonConfig
     *            the use json config
     * @return the JSON array
     * @see net.sf.json.JSONArray#fromObject(Object, JsonConfig)
     * @since 1.4.0
     */
    private static JSONArray toJSONArray(Object obj, JsonConfig useJsonConfig) {
        return JSONArray.fromObject(obj, useJsonConfig);
    }

    //**************toJSONObject********************

    /**
     * To json object.
     *
     * @param json
     *            the json
     * @return the JSON object
     * @see net.sf.json.JSONObject#fromObject(Object)
     * @since 1.4.0
     */
    private static JSONObject toJSONObject(String json) {
        return toJSONObject(json, new JsonConfig());
    }

    /**
     * To json object.
     *
     * @param obj
     *            Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans
     * @param useJsonConfig
     *            the use json config
     * @return the JSON object
     * @see net.sf.json.JSONObject#fromObject(Object, JsonConfig)
     * @since 1.4.0
     */
    private static JSONObject toJSONObject(Object obj, JsonConfig useJsonConfig) {
        return JSONObject.fromObject(obj, useJsonConfig);
    }

    // *****************************Array******************************************************
    // [start]toArray

    /**
     * json,??.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "[{'name':'get'},{'name':'set'}]";
     * Person[] objArr = JsonUtil.toArray(json, Person.class);
     * 
     * LOGGER.info(JsonUtil.format(objArr));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
       [{
           "dateAttr": null,
           "name": "get"
       },
       {
           "dateAttr": null,
           "name": "set"
    }]
     * 
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. [{'name':'get'},{'name':'set'}]
     * @param rootClass
     *            e.g. Person.class,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @return Object[]
     * @see #toArray(String, Class, Map)
     */
    public static <T> T[] toArray(String json, Class<T> rootClass) {
        return toArray(json, rootClass, null);
    }

    /**
     * json??,??Bean.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "[{'data':[{'name':'get'}]},{'data':[{'name':'set'}]}]";
     * Map{@code <String, Class<?>>} classMap = new HashMap{@code <String, Class<?>>}();
     * classMap.put("data", Person.class);
     * 
     * MyBean[] objArr = JsonUtil.toArray(json, MyBean.class, classMap);
     * LOGGER.info(JsonUtil.format(objArr));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
       [{
               "id": 0,
               "data": [            {
                   "dateAttr": null,
                   "name": "get"
               }]
     },{
               "id": 0,
               "data": [            {
                   "dateAttr": null,
                   "name": "set"
              }]
       }]
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. [{'data':[{'name':'get'}]},{'data':[{'name':'set'}]}]
     * @param rootClass
     *            e.g. MyBean.class,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @param classMap
     *            e.g. classMap.put("data", Person.class)
     * @return T[]
     * @see net.sf.json.JSONArray#fromObject(Object)
     * @see net.sf.json.JSONArray#getJSONObject(int)
     * @see #toBean(Object, Class, Map)
     * @see java.lang.reflect.Array#newInstance(Class, int)
     */
    public static <T> T[] toArray(String json, Class<T> rootClass, Map<String, Class<?>> classMap) {
        JSONArray jsonArray = toJSONArray(json);

        int size = jsonArray.size();
        T[] t = ArrayUtil.newArray(rootClass, size);
        for (int i = 0; i < size; i++) {
            t[i] = toBean(jsonArray.getJSONObject(i), rootClass, classMap);
        }
        return t;
    }

    // [end]

    // *****************************List********************************************************
    // [start]toList

    /**
     * json???,?Bean.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "[{'name':'get'},{'name':'set'}]";
     * List{@code <Person>} list = JsonUtil.toList(json, Person.class);
     * 
     * LOGGER.info(JsonUtil.format(list));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
       [{
           "dateAttr": null,
           "name": "get"
       },
               {
           "dateAttr": null,
           "name": "set"
       }
       ]
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. [{'name':'get'},{'name':'set'}]
     * @param rootClass
     *            the klass,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @return List
     * @see #toList(String, Class, Map)
     */
    public static <T> List<T> toList(String json, Class<T> rootClass) {
        return toList(json, rootClass, null);
    }

    /**
     * json???,???Bean.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "[{'data':[{'name':'get'}]},{'data':[{'name':'set'}]}]";
     * Map{@code <String, Class<?>>} classMap = new HashMap{@code <String, Class<?>>}();
     * classMap.put("data", Person.class);
     * 
     * List{@code <MyBean>} list = JsonUtil.toList(json, MyBean.class, classMap);
     * 
     * LOGGER.debug(JsonUtil.format(list));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     [{
         "id": 0,
         "data": [            {
             "dateAttr": null,
             "name": "get"
         }]
     },
             {
         "id": 0,
         "data": [            {
             "dateAttr": null,
             "name": "set"
         }]
      }]
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. [{'data':[{'name':'get'}]},{'data':[{'name':'set'}]}]
     * @param rootClass
     *            e.g. MyBean.class,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @param classMap
     *            e.g. classMap.put("data", Person.class)
     * @return List
     * @see net.sf.json.JSONArray#getJSONObject(int)
     * @see net.sf.json.JSONArray#fromObject(Object)
     * @see #toBean(Object, Class, Map)
     */
    public static <T> List<T> toList(String json, Class<T> rootClass, Map<String, Class<?>> classMap) {
        JSONArray jsonArray = toJSONArray(json);
        List<T> list = new ArrayList<T>();
        for (int i = 0, j = jsonArray.size(); i < j; i++) {
            list.add(toBean(jsonArray.getJSONObject(i), rootClass, classMap));
        }
        return list;
    }

    // [end]

    // ********************************Map******************************************************

    // [start]toMap

    /**
     * ? json?map.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "{'brandCode':'UA'}";
     * Map{@code <String, Object>} map = JsonUtil.toMap(json);
     * LOGGER.info(JsonUtil.format(map));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * key brandCode,value  UA map
     * </pre>
     * 
     * <hr>
     * 
     * <pre class="code">
     * Map{@code <String, Integer>} map2 = JsonUtil.toMap("{'brandCode':55555}");
     * LOGGER.info(JsonUtil.format(map2));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {"brandCode": 55555}
     * </pre>
     * 
     * </blockquote>
     * 
     * <h3>?:</h3>
     * 
     * <p>
     *  unchecked,??
     * </p>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, Long>} map3 = JsonUtil.toMap("{'brandCode':55.555}");
     * LOGGER.info(JsonUtil.format(map3));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {"brandCode": 55.555}
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            the json
     * @return  <code>json</code> nullempty, {@link Collections#emptyMap()}
     * @see #toMap(String, Class)
     * @since 1.5.0
     */
    public static <T> Map<String, T> toMap(String json) {
        return toMap(json, null);
    }

    /**
     * json??map,mapBean.
     * 
     * <p>
     *  null==klass,json?value map value
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "{'data1':{'name':'get'},'data2':{'name':'set'}}";
     * Map{@code <String, Person>} map = JsonUtil.toMap(json, Person.class);
     * LOGGER.info(JsonUtil.format(map));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     {"data1":{
             "name": "get"
         },
      "data2":{
             "name": "set"
     }}
     * </pre>
     * 
     * <hr>
     * 
     * <pre class="code">
     * Map{@code <String, String>} map1 = JsonUtil.toMap("{'data1':{'name':'get'},'data2':{'name':'set'}}", null);
     * LOGGER.info(JsonUtil.format(map1));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "data1": {"name": "get"},
     * "data2": {"name": "set"}
     * }
     * </pre>
     * 
     * </blockquote>
     * 
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. {'data1':{'name':'get'}, 'data2':{'name':'set'}}
     * @param rootClass
     *            e.g. Person.class ,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @return  <code>json</code> nullempty, {@link Collections#emptyMap()}
     * @see #toMap(String, Class, Map)
     */
    public static <T> Map<String, T> toMap(String json, Class<T> rootClass) {
        return toMap(json, rootClass, null);
    }

    /**
     * json??map,mapBean??Bean.
     * 
     * <p>
     *  null==klass,json?value map value
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "{'mybean':{'data':[{'name':'get'}]}}";
     * Map{@code <String, Class<?>>} classMap = new HashMap{@code <String, Class<?>>}();
     * classMap.put("data", Person.class);
     * 
     * Map{@code <String, MyBean>} map = JsonUtil.toMap(json, MyBean.class, classMap);
     * LOGGER.debug(JsonUtil.format(map));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
       {"mybean":{
             "id": 0,
             "data": [{
                 "name": "get"
             }]
       }}
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. {'mybean':{'data':[{'name':'get'}]}}
     * @param rootClass
     *            e.g. MyBean.class ,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @param classMap
     *            e.g. classMap.put("data", Person.class)
     * @return  <code>json</code> nullempty, {@link Collections#emptyMap()}<br>
     * @see net.sf.json.JSONObject#keys()
     * @see #toBean(Object, Class, Map)
     */
    @SuppressWarnings("unchecked")
    public static <T> Map<String, T> toMap(String json, Class<T> rootClass, Map<String, Class<?>> classMap) {
        LOGGER.trace("input json:[{}],rootClass:[{}]", json, rootClass);

        if (isNullOrEmpty(json)) {
            return Collections.emptyMap();
        }

        Map<String, T> map = new HashMap<String, T>();

        JSONObject jsonObject = toJSONObject(json);
        Iterator<String> keys = jsonObject.keys();
        while (keys.hasNext()) {
            String key = keys.next();
            Object value = jsonObject.get(key);
            LOGGER.debug("key:[{}],value:[{}],value type is:[{}]", key, value, value.getClass().getName());
            map.put(key, null == rootClass ? (T) value : toBean(value, rootClass, classMap));//klassnull,???
        }
        return map;
    }

    // [end]

    //***********************************************************************************
    // [start]toBean

    /**
     * json,??.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "{'name':'get','dateAttr':'2009-11-12'}";
     * LOGGER.debug(JsonUtil.format(JsonUtil.toBean(json, Person.class)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "dateAttr": "2009-11-12 00:00:00",
     * "name": "get"
     * }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. {'name':'get','dateAttr':'2009-11-12'}<br>
     *            ? json,?JSONObject<br>
     *            Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans. <br>
     *            ??: {@link JSONObject#fromObject(Object, JsonConfig)}
     * @param rootClass
     *            e.g. Person.class,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @return the t
     */
    public static <T> T toBean(Object json, Class<T> rootClass) {
        return toBean(json, rootClass, null);
    }

    /**
     * json??,??Bean.
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * String json = "{'data':[{'name':'get'},{'name':'set'}],'id':5}";
     * Map{@code <String, Class<?>>} classMap = new HashMap{@code <String, Class<?>>}();
     * classMap.put("data", Person.class);
     * 
     * MyBean myBean = JsonUtil.toBean(json, MyBean.class, classMap);
     * LOGGER.info(JsonUtil.format(myBean));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     {
         "id": 5,
         "data":[{
                 "dateAttr": null,
                 "name": "get"
             },{
                 "dateAttr": null,
                 "name": "set"
             }
         ]
      }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <T>
     *            the generic type
     * @param json
     *            e.g. {'data':[{'name':'get'},{'name':'set'}]}
     * @param rootClass
     *            e.g. MyBean.class,see {@link net.sf.json.JsonConfig#setRootClass(Class)}
     * @param classMap
     *            e.g. classMap.put("data", Person.class)
     * @return Object
     * @see #toBean(Object, JsonConfig)
     */
    public static <T> T toBean(Object json, Class<T> rootClass, Map<String, Class<?>> classMap) {
        JSONObject jsonObject = JSONObject.fromObject(json);

        JsonConfig jsonConfig = getDefaultJsonConfig();
        jsonConfig.setRootClass(rootClass);

        if (isNotNullOrEmpty(classMap)) {
            jsonConfig.setClassMap(classMap);
        }
        return toBean(jsonObject, jsonConfig);
    }

    /**
     * json,??.
     *
     * @param <T>
     *            the generic type
     * @param json
     *            the json
     * @param jsonConfig
     *            the json config
     * @return the object
     * @see net.sf.json.JSONObject#toBean(JSONObject, JsonConfig)
     */
    @SuppressWarnings("unchecked")
    private static <T> T toBean(Object json, JsonConfig jsonConfig) {
        JSONObject jsonObject = JSONObject.fromObject(json);

        // Ignore missing properties with Json-Lib

        // ?? Unknown property 'orderIdAndCodeMap' on class 'class
        // com.baozun.trade.web.controller.payment.result.command.PaymentResultEntity' 
        jsonConfig.setPropertySetStrategy(new PropertyStrategyWrapper(PropertySetStrategy.DEFAULT));
        return (T) JSONObject.toBean(jsonObject, jsonConfig);
    }

    // [end]
    //***********************************************************************************
    /**
     * JsonConfig.
     *
     * @return the default json config
     * @see see net.sf.json.JsonConfig#DEFAULT_EXCLUDES
     * 
     * @see net.sf.json.util.CycleDetectionStrategy#LENIENT
     */
    private static JsonConfig getDefaultJsonConfig() {
        JsonConfig jsonConfig = new JsonConfig();

        // ,?? There is a cycle in the hierarchy!
        //Returns empty array and null object
        jsonConfig.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);

        //see net.sf.json.JsonConfig#DEFAULT_EXCLUDES
        //key "class","declaringClass","metaClass"
        jsonConfig.setIgnoreDefaultExcludes(false);

        //See javax.persistence.Transient
        //jsonConfig.setIgnoreJPATransient(true);

        //see Modifier.TRANSIENT
        //jsonConfig.setIgnoreTransientFields(true);

        //jsonConfig.setIgnorePublicFields(false);

        // ?
        jsonConfig.registerJsonValueProcessor(Date.class, new DateJsonValueProcessor(COMMON_DATE_AND_TIME));

        // java.lang.ClassCastException: JSON keys must be strings
        // see http://feitianbenyue.iteye.com/blog/2046877
        jsonConfig.setAllowNonStringKeys(true);
        return jsonConfig;
    }

    /**
     * ??json format.
     *
     * @param <V>
     *            the value type
     * @param value
     *            the value
     * @param allowClassTypes
     *            the allow class types
     * @return true, if checks if is allow format type
     * @since 1.4.0
     */
    private static <V> boolean isAllowFormatType(V value, Class<?>... allowClassTypes) {
        if (null == value) {//null ? format
            return true;
        }
        Class<?> klassClass = value.getClass();
        return ClassUtils.isPrimitiveOrWrapper(klassClass) //
                || String.class == klassClass //
                || ObjectUtil.isArray(value)//XXX  ?? 
                || ClassUtil.isInstanceAnyClass(value, allowClassTypes)//
        ;
    }
}