This commit is contained in:
Looly
2020-04-17 15:54:43 +08:00
parent 94c824e03d
commit 7e8096a75c
6 changed files with 242 additions and 144 deletions

View File

@@ -13,6 +13,7 @@
* 【core 】 增加XmlUtil.setNamespaceAwaregetByPath支持UniversalNamespaceCache * 【core 】 增加XmlUtil.setNamespaceAwaregetByPath支持UniversalNamespaceCache
* 【core 】 增加XmlUtil.setNamespaceAwaregetByPath支持UniversalNamespaceCache * 【core 】 增加XmlUtil.setNamespaceAwaregetByPath支持UniversalNamespaceCache
* 【aop 】 增加Spring-cglib支持改为SPI实现 * 【aop 】 增加Spring-cglib支持改为SPI实现
* 【json 】 增加JSONUtil.parseXXX增加JSONConfig参数
### Bug修复 ### Bug修复
* 【json 】 修复解析JSON字符串时配置无法传递问题 * 【json 】 修复解析JSON字符串时配置无法传递问题

View File

@@ -188,8 +188,7 @@ final class InternalJSONUtil {
* @return JSONObject * @return JSONObject
*/ */
protected static JSONObject propertyPut(JSONObject jsonObject, Object key, Object value) { protected static JSONObject propertyPut(JSONObject jsonObject, Object key, Object value) {
String keyStr = Convert.toStr(key); final String[] path = StrUtil.split(Convert.toStr(key), StrUtil.DOT);
String[] path = StrUtil.split(keyStr, StrUtil.DOT);
int last = path.length - 1; int last = path.length - 1;
JSONObject target = jsonObject; JSONObject target = jsonObject;
for (int i = 0; i < last; i += 1) { for (int i = 0; i < last; i += 1) {

View File

@@ -1,13 +1,13 @@
package cn.hutool.json; package cn.hutool.json;
import cn.hutool.core.bean.BeanPath;
import cn.hutool.core.lang.TypeReference;
import java.io.Serializable; import java.io.Serializable;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import cn.hutool.core.bean.BeanPath;
import cn.hutool.core.lang.TypeReference;
/** /**
* JSON接口 * JSON接口
* *

View File

@@ -23,8 +23,10 @@ import java.lang.reflect.Method;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Collection; import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
/** /**
@@ -40,15 +42,22 @@ import java.util.Set;
public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object> { public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object> {
private static final long serialVersionUID = -330220388580734346L; private static final long serialVersionUID = -330220388580734346L;
/** 默认初始大小 */ /**
public static final int DEFAULT_CAPACITY = 16; * 默认初始大小
*/
public static final int DEFAULT_CAPACITY = MapUtil.DEFAULT_INITIAL_CAPACITY;
/** JSON的KV持有Map */ /**
* JSON的KV持有Map
*/
private final Map<String, Object> rawHashMap; private final Map<String, Object> rawHashMap;
/** 配置项 */ /**
* 配置项
*/
private final JSONConfig config; private final JSONConfig config;
// -------------------------------------------------------------------------------------------------------------------- Constructor start // -------------------------------------------------------------------------------------------------------------------- Constructor start
/** /**
* 构造,初始容量为 {@link #DEFAULT_CAPACITY}KEY无序 * 构造,初始容量为 {@link #DEFAULT_CAPACITY}KEY无序
*/ */
@@ -179,7 +188,7 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
* <li>value为JSONTokener直接解析</li> * <li>value为JSONTokener直接解析</li>
* <li>value为普通JavaBean如果为普通的JavaBean调用其getters方法getXXX或者isXXX获得值加入到JSON对象。例如如果JavaBean对象中有个方法getName(),值为"张三"获得的键值对为name: "张三"</li> * <li>value为普通JavaBean如果为普通的JavaBean调用其getters方法getXXX或者isXXX获得值加入到JSON对象。例如如果JavaBean对象中有个方法getName(),值为"张三"获得的键值对为name: "张三"</li>
* </ol> * </ol>
* * <p>
* 如果给定值为Map将键值对加入JSON对象;<br> * 如果给定值为Map将键值对加入JSON对象;<br>
* 如果为普通的JavaBean调用其getters方法getXXX或者isXXX获得值加入到JSON对象<br> * 如果为普通的JavaBean调用其getters方法getXXX或者isXXX获得值加入到JSON对象<br>
* 例如如果JavaBean对象中有个方法getName(),值为"张三"获得的键值对为name: "张三" * 例如如果JavaBean对象中有个方法getName(),值为"张三"获得的键值对为name: "张三"
@@ -200,7 +209,7 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
* 1. 若obj为Map则获取name列表对应键值对 * 1. 若obj为Map则获取name列表对应键值对
* 2. 若obj为普通Bean使用反射方式获取字段名和字段值 * 2. 若obj为普通Bean使用反射方式获取字段名和字段值
* </pre> * </pre>
* * <p>
* KEY或VALUE任意一个为null则不加入字段不存在也不加入<br> * KEY或VALUE任意一个为null则不加入字段不存在也不加入<br>
* 若names列表为空则字段全部加入 * 若names列表为空则字段全部加入
* *
@@ -236,7 +245,7 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
* *
* @param source 以大括号 {} 包围的字符串其中KEY和VALUE使用 : 分隔,每个键值对使用逗号分隔 * @param source 以大括号 {} 包围的字符串其中KEY和VALUE使用 : 分隔,每个键值对使用逗号分隔
* @param isOrder 是否有序 * @param isOrder 是否有序
* @exception JSONException JSON字符串语法错误 * @throws JSONException JSON字符串语法错误
* @since 4.2.2 * @since 4.2.2
*/ */
public JSONObject(CharSequence source, boolean isOrder) throws JSONException { public JSONObject(CharSequence source, boolean isOrder) throws JSONException {
@@ -559,6 +568,7 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
} }
// ------------------------------------------------------------------------------------------------- Private method start // ------------------------------------------------------------------------------------------------- Private method start
/** /**
* 将JSON内容写入Writer * 将JSON内容写入Writer
* *
@@ -678,6 +688,9 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
} else if (source instanceof JSONTokener) { } else if (source instanceof JSONTokener) {
// JSONTokener // JSONTokener
init((JSONTokener) source); init((JSONTokener) source);
} else if (source instanceof ResourceBundle) {
// JSONTokener
init((ResourceBundle) source);
} else if (BeanUtil.isReadableBean(source.getClass())) { } else if (BeanUtil.isReadableBean(source.getClass())) {
// 普通Bean // 普通Bean
this.populateMap(source); this.populateMap(source);
@@ -687,9 +700,30 @@ public class JSONObject implements JSON, JSONGetter<String>, Map<String, Object>
/** /**
* 初始化 * 初始化
* *
* @param bundle ResourceBundle
* @since 5.3.1
*/
private void init(ResourceBundle bundle) {
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
if (key != null) {
InternalJSONUtil.propertyPut(this, key, bundle.getString(key));
}
}
}
/**
* 初始化可以判断字符串为JSON或者XML
*
* @param source JSON字符串 * @param source JSON字符串
*/ */
private void init(CharSequence source) { private void init(CharSequence source) {
final String jsonStr = StrUtil.trim(source);
if (StrUtil.startWith(jsonStr, '<')) {
// 可能为XML
XML.toJSONObject(this, jsonStr, false);
}
init(new JSONTokener(StrUtil.trim(source), this.config)); init(new JSONTokener(StrUtil.trim(source), this.config));
} }

View File

@@ -23,9 +23,8 @@ import java.lang.reflect.Type;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Enumeration; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@@ -100,6 +99,19 @@ public final class JSONUtil {
return new JSONObject(obj); return new JSONObject(obj);
} }
/**
* JSON字符串转JSONObject对象<br>
* 此方法会忽略空值但是对JSON字符串不影响
*
* @param obj Bean对象或者Map
* @param config JSON配置
* @return JSONObject
* @since 5.3.1
*/
public static JSONObject parseObj(Object obj, JSONConfig config) {
return new JSONObject(obj, config);
}
/** /**
* JSON字符串转JSONObject对象 * JSON字符串转JSONObject对象
* *
@@ -146,6 +158,18 @@ public final class JSONUtil {
return new JSONArray(arrayOrCollection); return new JSONArray(arrayOrCollection);
} }
/**
* JSON字符串转JSONArray
*
* @param arrayOrCollection 数组或集合对象
* @param config JSON配置
* @return JSONArray
* @since 5.3.1
*/
public static JSONArray parseArray(Object arrayOrCollection, JSONConfig config) {
return new JSONArray(arrayOrCollection, config);
}
/** /**
* JSON字符串转JSONArray * JSON字符串转JSONArray
* *
@@ -169,6 +193,22 @@ public final class JSONUtil {
* @return JSON * @return JSON
*/ */
public static JSON parse(Object obj) { public static JSON parse(Object obj) {
return parse(obj, JSONConfig.create());
}
/**
* 转换对象为JSON<br>
* 支持的对象:<br>
* String: 转换为相应的对象<br>
* Array、Iterable、Iterator转换为JSONArray<br>
* Bean对象转为JSONObject
*
* @param obj 对象
* @param config JSON配置
* @return JSON
* @since 5.3.1
*/
public static JSON parse(Object obj, JSONConfig config) {
if (null == obj) { if (null == obj) {
return null; return null;
} }
@@ -176,17 +216,13 @@ public final class JSONUtil {
JSON json; JSON json;
if (obj instanceof JSON) { if (obj instanceof JSON) {
json = (JSON) obj; json = (JSON) obj;
} else if (obj instanceof String) { } else if (obj instanceof CharSequence) {
String jsonStr = ((String) obj).trim(); final String jsonStr = StrUtil.trim((CharSequence) obj);
if (jsonStr.startsWith("[")) { json = StrUtil.startWith(jsonStr, '[') ? parseArray(jsonStr) : parseObj(jsonStr);
json = parseArray(jsonStr); } else if (obj instanceof Iterable || obj instanceof Iterator || ArrayUtil.isArray(obj)) {// 列表
} else { json = new JSONArray(obj, config);
json = parseObj(jsonStr);
}
} else if (obj instanceof Collection || obj.getClass().isArray()) {// 列表
json = new JSONArray(obj);
} else {// 对象 } else {// 对象
json = new JSONObject(obj); json = new JSONObject(obj, config);
} }
return json; return json;
@@ -206,8 +242,10 @@ public final class JSONUtil {
* Map转化为JSONObject * Map转化为JSONObject
* *
* @param map {@link Map} * @param map {@link Map}
* @return JSONObject * @return JSONObjec
* @deprecated 请直接使用 {@link #parseObj(Object)}
*/ */
@Deprecated
public static JSONObject parseFromMap(Map<?, ?> map) { public static JSONObject parseFromMap(Map<?, ?> map) {
return new JSONObject(map); return new JSONObject(map);
} }
@@ -217,17 +255,11 @@ public final class JSONUtil {
* *
* @param bundle ResourceBundle文件 * @param bundle ResourceBundle文件
* @return JSONObject * @return JSONObject
* @deprecated 请直接使用 {@link #parseObj(Object)}
*/ */
@Deprecated
public static JSONObject parseFromResourceBundle(ResourceBundle bundle) { public static JSONObject parseFromResourceBundle(ResourceBundle bundle) {
JSONObject jsonObject = new JSONObject(); return new JSONObject(bundle);
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
if (key != null) {
InternalJSONUtil.propertyPut(jsonObject, key, bundle.getString(key));
}
}
return jsonObject;
} }
// -------------------------------------------------------------------- Pause end // -------------------------------------------------------------------- Pause end

View File

@@ -14,33 +14,98 @@ import java.util.Iterator;
*/ */
public class XML { public class XML {
/** The Character '&amp;'. */ /**
* The Character '&amp;'.
*/
public static final Character AMP = CharUtil.AMP; public static final Character AMP = CharUtil.AMP;
/** The Character '''. */ /**
* The Character '''.
*/
public static final Character APOS = CharUtil.SINGLE_QUOTE; public static final Character APOS = CharUtil.SINGLE_QUOTE;
/** The Character '!'. */ /**
* The Character '!'.
*/
public static final Character BANG = '!'; public static final Character BANG = '!';
/** The Character '='. */ /**
* The Character '='.
*/
public static final Character EQ = '='; public static final Character EQ = '=';
/** The Character '&gt;'. */ /**
* The Character '&gt;'.
*/
public static final Character GT = '>'; public static final Character GT = '>';
/** The Character '&lt;'. */ /**
* The Character '&lt;'.
*/
public static final Character LT = '<'; public static final Character LT = '<';
/** The Character '?'. */ /**
* The Character '?'.
*/
public static final Character QUEST = '?'; public static final Character QUEST = '?';
/** The Character '"'. */ /**
* The Character '"'.
*/
public static final Character QUOT = CharUtil.DOUBLE_QUOTES; public static final Character QUOT = CharUtil.DOUBLE_QUOTES;
/** The Character '/'. */ /**
* The Character '/'.
*/
public static final Character SLASH = CharUtil.SLASH; public static final Character SLASH = CharUtil.SLASH;
/**
* 转换XML为JSONObject
* 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]&gt;</code> are ignored.
*
* @param string The source string.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(String string) throws JSONException {
return toJSONObject(string, false);
}
/**
* 转换XML为JSONObject
* 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]&gt;</code> are ignored.
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to numbers but will instead be the exact value as seen in the XML document.
*
* @param string The source string.
* @param keepStrings If true, then values will not be coerced into boolean or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
return toJSONObject(new JSONObject(), string, keepStrings);
}
/**
* 转换XML为JSONObject
* 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。
*
* @param jo JSONObject
* @param string XML字符串
* @param keepStrings If true, then values will not be coerced into boolean or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
* @since 5.3.1
*/
public static JSONObject toJSONObject(JSONObject jo, String string, boolean keepStrings) throws JSONException {
XMLTokener x = new XMLTokener(string, jo.getConfig());
while (x.more() && x.skipPast("<")) {
parse(x, jo, null, keepStrings);
}
return jo;
}
/** /**
* Scan the content following the named tag, attaching it to the context. * Scan the content following the named tag, attaching it to the context.
* *
@@ -203,39 +268,6 @@ public class XML {
} }
} }
/**
* 转换XML为JSONObject
* 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]&gt;</code> are ignored.
*
* @param string The source string.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(String string) throws JSONException {
return toJSONObject(string, false);
}
/**
* 转换XML为JSONObject
* 转换过程中一些信息可能会丢失JSON中无法区分节点和属性相同的节点将被处理为JSONArray。
* Content text may be placed in a "content" member. Comments, prologs, DTDs, and <code>&lt;[ [ ]]&gt;</code> are ignored.
* All values are converted as strings, for 1, 01, 29.0 will not be coerced to numbers but will instead be the exact value as seen in the XML document.
*
* @param string The source string.
* @param keepStrings If true, then values will not be coerced into boolean or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown if there is an errors while parsing the string
*/
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
JSONObject jo = new JSONObject();
XMLTokener x = new XMLTokener(string, jo.getConfig());
while (x.more() && x.skipPast("<")) {
parse(x, jo, null, keepStrings);
}
return jo;
}
/** /**
* 转换JSONObject为XML * 转换JSONObject为XML
* Convert a JSONObject into a well-formed, element-normal XML string. * Convert a JSONObject into a well-formed, element-normal XML string.