package com.xiaoleilu.hutool.json;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.xiaoleilu.hutool.convert.Convert;
import com.xiaoleilu.hutool.util.ClassUtil;
import com.xiaoleilu.hutool.util.CollectionUtil;
import com.xiaoleilu.hutool.util.StrUtil;
/**
* JSON对象<br>
* 例:<br>
*<pre>
* json = new JSONObject().put("JSON", "Hello, World!").toString();
* </pre>
* @author looly
*/
public class JSONObject extends JSONGetter<String> implements JSON, Map<String, Object> {
/** JSON的KV持有Map */
private final Map<String, Object> rawHashMap = new HashMap<>();
/**
* 空构造
*/
public JSONObject() {
}
// -------------------------------------------------------------------------------------------------------------------- Constructor start
/**
* 使用其他<code>JSONObject</code>构造新的<code>JSONObject</code>,并只加入指定name对应的键值对。
*
* @param jsonObject A JSONObject.
* @param names 需要的name列表
*/
public JSONObject(JSONObject jsonObject, String... names) {
for (String name : names) {
try {
this.putOnce(name, jsonObject.getObj(name));
} catch (Exception ignore) {
}
}
}
/**
* 使用{@link JSONTokener}构建
*
* @param x {@link JSONTokener}
* @throws JSONException 语法错误
*/
public JSONObject(JSONTokener x) throws JSONException {
init(x);
}
/**
* 构建JSONObject,如果给定值为Map,将键值对加入JSON对象;<br>
* 如果为普通的JavaBean,调用其getters方法(getXXX或者isXXX)获得值,加入到JSON对象<br>
* 例如:如果JavaBean对象中有个方法getName(),值为"张三",获得的键值对为:name: "张三"
*
* @param source JavaBean或者Map对象或者String
*/
public JSONObject(Object source) {
if (null != source) {
if (source instanceof Map) {
for (final Entry<?, ?> e : ((Map<?, ?>) source).entrySet()) {
final Object value = e.getValue();
if (value != null) {
this.rawHashMap.put(Convert.toStr(e.getKey()), JSONUtil.wrap(value));
}
}
}else if(source instanceof String){
init((String)source);
} else {
this.populateMap(source);
}
}
}
/**
* 使用反射方式获取public字段名和字段值,构建JSONObject对象<br>
* KEY或VALUE任意一个为null则不加入
*
* @param pojo 包含需要字段的Bean对象
* @param names 需要构建JSONObject的字段名列表
*/
public JSONObject(Object pojo, String[] names) {
Class<?> c = pojo.getClass();
for (String name : names) {
try {
this.putOpt(name, c.getField(name).get(pojo));
} catch (Exception ignore) {
}
}
}
/**
* 从JSON字符串解析为JSON对象
*
* @param source 以大括号 {} 包围的字符串,其中KEY和VALUE使用 : 分隔,每个键值对使用逗号分隔
* @exception JSONException JSON字符串语法错误
*/
public JSONObject(String source) throws JSONException {
this(new JSONTokener(source));
}
// -------------------------------------------------------------------------------------------------------------------- Constructor end
/**
* key对应值是否为<code>null</code>或无此key
*
* @param key 键
* @return true 无此key或值为<code>null</code>或{@link JSONNull#NULL}返回<code>false</code>,其它返回<code>true</code>
*/
public boolean isNull(String key) {
return JSONNull.NULL.equals(this.getObj(key));
}
/**
* 将指定KEY列表的值组成新的JSONArray
*
* @param names KEY列表
* @return A JSONArray of values.
* @throws JSONException If any of the values are non-finite numbers.
*/
public JSONArray toJSONArray(Collection<String> names) throws JSONException {
if (CollectionUtil.isEmpty(names)) {
return null;
}
final JSONArray ja = new JSONArray();
Object value;
for (String name : names) {
value = this.get(name);
if(null != value){
ja.put(value);
}
}
return ja;
}
/**
* 转为实体类对象,转换异常将被抛出
*
* @param clazz 实体类
* @return 实体类对象
*/
public <T> T toBean(Class<T> clazz) {
return toBean(clazz, false);
}
/**
* 转为实体类对象
*
* @param clazz 实体类
* @param ignoreError 是否忽略转换错误
* @return 实体类对象
*/
public <T> T toBean(Class<T> clazz, boolean ignoreError) {
return toBean(ClassUtil.newInstance(clazz), ignoreError);
}
/**
* 转为实体类对象,转换异常将被抛出
*
* @param bean 实体类
* @return 实体类对象
*/
public <T> T toBean(T bean) {
return toBean(bean, false);
}
/**
* 转为实体类对象
*
* @param bean 实体类
* @param ignoreError 是否忽略转换错误
* @return 实体类对象
*/
public <T> T toBean(T bean, boolean ignoreError) {
return InternalJSONUtil.toBean(this, bean, ignoreError);
}
@Override
public int size() {
return rawHashMap.size();
}
@Override
public boolean isEmpty() {
return rawHashMap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return rawHashMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return rawHashMap.containsValue(value);
}
@Override
public Object get(Object key) {
return rawHashMap.get(key);
}
@Override
public Object getObj(String key, Object defaultValue) {
Object obj = this.rawHashMap.get(key);
return null == obj ? defaultValue : obj;
}
/**
* PUT 键值对到JSONObject中,如果值为<code>null</code>,将此键移除
*
* @param key A key string.
* @param value 值对象. 可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
* @return this.
* @throws JSONException 值是无穷数字抛出此异常
*/
@Override
public JSONObject put(String key, Object value) throws JSONException {
if (key == null) {
throw new NullPointerException("Null key.");
}
if (value != null) {
InternalJSONUtil.testValidity(value);
this.rawHashMap.put(key, JSONUtil.wrap(value));
} else {
this.remove(key);
}
return this;
}
/**
* Put a key/value pair in the JSONObject, but only if the key and the value are both non-null, <br>
* and only if there is not already a member with that name.
* 一次性Put 键值对,如果key已经存在抛出异常,如果键值中有null值,忽略
*
* @param key 键
* @param value 值对象,可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
* @return this.
* @throws JSONException 值是无穷数字、键重复抛出异常
*/
public JSONObject putOnce(String key, Object value) throws JSONException {
if (key != null && value != null) {
if (rawHashMap.containsKey(key)) {
throw new JSONException(StrUtil.format("Duplicate key \"{}\"", key));
}
this.put(key, value);
}
return this;
}
/**
* 在键和值都为非空的情况下put到JSONObject中
*
* @param key 键
* @param value 值对象,可以是以下类型: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, or the JSONNull.NULL.
* @return this.
* @throws JSONException 值是无穷数字
*/
public JSONObject putOpt(String key, Object value) throws JSONException {
if (key != null && value != null) {
this.put(key, value);
}
return this;
}
@Override
public void putAll(Map<? extends String, ? extends Object> m) {
rawHashMap.putAll(m);
}
/**
* 积累值。类似于put,当key对应value已经存在时,与value组成新的JSONArray. <br>
* 如果只有一个值,此值就是value,如果多个值,则是添加到新的JSONArray中
*
* @param key A key string.
* @param value An object to be accumulated under the key.
* @return this.
* @throws JSONException If the value is an invalid number or if the key is null.
*/
public JSONObject accumulate(String key, Object value) throws JSONException {
InternalJSONUtil.testValidity(value);
Object object = this.getObj(key);
if (object == null) {
this.put(key, value instanceof JSONArray ? new JSONArray().put(value) : value);
} else if (object instanceof JSONArray) {
((JSONArray) object).put(value);
} else {
this.put(key, new JSONArray().put(object).put(value));
}
return this;
}
/**
* 追加值,如果key无对应值,就添加一个JSONArray,其元素只有value,如果值已经是一个JSONArray,则添加到值JSONArray中。
*
* @param key A key string.
* @param value An object to be accumulated under the key.
* @return this.
* @throws JSONException 如果给定键为<code>null</code>或者键对应的值存在且为非JSONArray
*/
public JSONObject append(String key, Object value) throws JSONException {
InternalJSONUtil.testValidity(value);
Object object = this.getObj(key);
if (object == null) {
this.put(key, new JSONArray().put(value));
} else if (object instanceof JSONArray) {
this.put(key, ((JSONArray) object).put(value));
} else {
throw new JSONException("JSONObject [" + key + "] is not a JSONArray.");
}
return this;
}
/**
* 对值加一,如果值不存在,赋值1,如果为数字类型,做加一操作
*
* @param key A key string.
* @return this.
* @throws JSONException If there is already a property with this name that is not an Integer, Long, Double, or Float.
*/
public JSONObject increment(String key) throws JSONException {
Object value = this.getObj(key);
if (value == null) {
this.put(key, 1);
} else if (value instanceof BigInteger) {
this.put(key, ((BigInteger) value).add(BigInteger.ONE));
} else if (value instanceof BigDecimal) {
this.put(key, ((BigDecimal) value).add(BigDecimal.ONE));
} else if (value instanceof Integer) {
this.put(key, (Integer) value + 1);
} else if (value instanceof Long) {
this.put(key, (Long) value + 1);
} else if (value instanceof Double) {
this.put(key, (Double) value + 1);
} else if (value instanceof Float) {
this.put(key, (Float) value + 1);
} else {
throw new JSONException("Unable to increment [" + JSONUtil.quote(key) + "].");
}
return this;
}
@Override
public Object remove(Object key) {
return rawHashMap.remove(key);
}
@Override
public void clear() {
rawHashMap.clear();
}
@Override
public Set<String> keySet() {
return this.rawHashMap.keySet();
}
@Override
public Collection<Object> values() {
return rawHashMap.values();
}
@Override
public Set<Entry<String, Object>> entrySet() {
return rawHashMap.entrySet();
}
/**
*JSON中的key以及对应的value相等则判定为与此对象相同
*
* @param other The other JSONObject
* @return true if they are equal
*/
@Override
public boolean equals(Object other) {
try {
if (!(other instanceof JSONObject)) {
return false;
}
Set<String> set = this.keySet();
if (!set.equals(((JSONObject) other).keySet())) {
return false;
}
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
Object valueThis = this.getObj(name);
Object valueOther = ((JSONObject) other).getObj(name);
if (valueThis instanceof JSONObject) {
if (!((JSONObject) valueThis).equals(valueOther)) {
return false;
}
} else if (valueThis instanceof JSONArray) {
if (!((JSONArray) valueThis).equals(valueOther)) {
return false;
}
} else if (!valueThis.equals(valueOther)) {
return false;
}
}
return true;
} catch (Throwable exception) {
return false;
}
}
/**
* 返回JSON字符串<br>
* 如果解析错误,返回<code>null</code>
*
* @return JSON字符串
*/
@Override
public String toString() {
try {
return this.toJSONString(0);
} catch (Exception e) {
return null;
}
}
/**
* 格式化输出JSON字符串
*
* @param indentFactor 每层缩进空格数
* @return JSON字符串
* @throws JSONException 包含非法数抛出此异常
*/
@Override
public String toJSONString(int indentFactor) throws JSONException {
StringWriter w = new StringWriter();
synchronized (w.getBuffer()) {
return this.write(w, indentFactor, 0).toString();
}
}
@Override
public Writer write(Writer writer) throws JSONException {
return this.write(writer, 0, 0);
}
@Override
public Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
try {
boolean commanate = false;
final int length = this.size();
Iterator<String> keys = this.keySet().iterator();
writer.write('{');
if (length == 1) {
Object key = keys.next();
writer.write(JSONUtil.quote(key.toString()));
writer.write(':');
if (indentFactor > 0) {
writer.write(' ');
}
InternalJSONUtil.writeValue(writer, this.rawHashMap.get(key), indentFactor, indent);
} else if (length != 0) {
final int newindent = indent + indentFactor;
while (keys.hasNext()) {
Object key = keys.next();
if (commanate) {
writer.write(',');
}
if (indentFactor > 0) {
writer.write('\n');
}
InternalJSONUtil.indent(writer, newindent);
writer.write(JSONUtil.quote(key.toString()));
writer.write(':');
if (indentFactor > 0) {
writer.write(' ');
}
InternalJSONUtil.writeValue(writer, this.rawHashMap.get(key), indentFactor, newindent);
commanate = true;
}
if (indentFactor > 0) {
writer.write('\n');
}
InternalJSONUtil.indent(writer, indent);
}
writer.write('}');
return writer;
} catch (IOException exception) {
throw new JSONException(exception);
}
}
// ------------------------------------------------------------------------------------------------- Private method start
/**
* Bean对象转Map
*
* @param bean Bean对象
*/
private void populateMap(Object bean) {
Class<?> clazz = bean.getClass();
// If klass is a System class then set includeSuperClass to false.
boolean includeSuperClass = clazz.getClassLoader() != null;
Method[] methods = includeSuperClass ? clazz.getMethods() : clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i += 1) {
try {
Method method = methods[i];
if (Modifier.isPublic(method.getModifiers())) {
String name = method.getName();
String key = "";
if (name.startsWith("get")) {
if ("getClass".equals(name) || "getDeclaringClass".equals(name)) {
key = "";
} else {
key = name.substring(3);
}
} else if (name.startsWith("is")) {
key = name.substring(2);
}
if (key.length() > 0 && Character.isUpperCase(key.charAt(0)) && method.getParameterTypes().length == 0) {
if (key.length() == 1) {
key = key.toLowerCase();
} else if (!Character.isUpperCase(key.charAt(1))) {
key = key.substring(0, 1).toLowerCase() + key.substring(1);
}
Object result = method.invoke(bean, (Object[]) null);
if (result != null) {
this.rawHashMap.put(key, JSONUtil.wrap(result));
}
}
}
} catch (Exception ignore) {
}
}
}
/**
* 初始化
* @param source JSON字符串
*/
private void init(String source){
init(new JSONTokener(source));
}
/**
* 初始化
* @param x JSONTokener
*/
private void init(JSONTokener x) {
char c;
String key;
if (x.nextClean() != '{') {
throw x.syntaxError("A JSONObject text must begin with '{'");
}
for (;;) {
c = x.nextClean();
switch (c) {
case 0:
throw x.syntaxError("A JSONObject text must end with '}'");
case '}':
return;
default:
x.back();
key = x.nextValue().toString();
}
// The key is followed by ':'.
c = x.nextClean();
if (c != ':') {
throw x.syntaxError("Expected a ':' after a key");
}
this.putOnce(key, x.nextValue());
// Pairs are separated by ','.
switch (x.nextClean()) {
case ';':
case ',':
if (x.nextClean() == '}') {
return;
}
x.back();
break;
case '}':
return;
default:
throw x.syntaxError("Expected a ',' or '}'");
}
}
}
// ------------------------------------------------------------------------------------------------- Private method end
}