diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONParser.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONParser.java
index 7eb63b151..5b4055a2b 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONParser.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONParser.java
@@ -30,14 +30,20 @@ import java.util.function.Predicate;
*/
public class JSONParser {
+ /**
+ * JSON配置
+ */
+ private final JSONConfig config;
+
/**
* 创建JSONParser
*
* @param tokener {@link JSONTokener}
+ * @param config JSON配置
* @return JSONParser
*/
- public static JSONParser of(final JSONTokener tokener) {
- return new JSONParser(tokener);
+ public static JSONParser of(final JSONTokener tokener, final JSONConfig config) {
+ return new JSONParser(tokener, config);
}
private final JSONTokener tokener;
@@ -46,9 +52,29 @@ public class JSONParser {
* 构造
*
* @param tokener {@link JSONTokener}
+ * @param config JSON配置
*/
- public JSONParser(final JSONTokener tokener) {
+ public JSONParser(final JSONTokener tokener, final JSONConfig config) {
this.tokener = tokener;
+ this.config = config;
+ }
+
+ /**
+ * 获取{@link JSONTokener}
+ *
+ * @return {@link JSONTokener}
+ */
+ public JSONTokener getTokener() {
+ return this.tokener;
+ }
+
+ /**
+ * 是否结束
+ *
+ * @return 是否结束
+ */
+ public boolean end() {
+ return this.tokener.end();
}
// region parseTo
@@ -79,12 +105,12 @@ public class JSONParser {
return;
case '{':
case '[':
- if(prev=='{') {
+ if (prev == '{') {
throw tokener.syntaxError("A JSONObject can not directly nest another JSONObject or JSONArray.");
}
default:
tokener.back();
- key = tokener.nextValue(true).toString();
+ key = nextValue(true).toString();
}
// The key is followed by ':'.
@@ -94,7 +120,7 @@ public class JSONParser {
throw tokener.syntaxError("Expected a ':' after a key");
}
- jsonObject.set(key, tokener.nextValue(false), predicate);
+ jsonObject.set(key, nextValue(false), predicate);
// Pairs are separated by ','.
@@ -136,7 +162,7 @@ public class JSONParser {
jsonArray.addRaw(null, predicate);
} else {
x.back();
- jsonArray.addRaw(x.nextValue(false), predicate);
+ jsonArray.addRaw(nextValue(false), predicate);
}
switch (x.nextClean()) {
case CharUtil.COMMA:
@@ -154,4 +180,91 @@ public class JSONParser {
}
}
// endregion
+
+ /**
+ * 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
+ *
+ * @param getOnlyStringValue 是否只获取String值
+ * @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
+ * @throws JSONException 语法错误
+ */
+ public Object nextValue(final boolean getOnlyStringValue) throws JSONException {
+ return nextValue(getOnlyStringValue, (token, tokener, config) -> {
+ switch (token) {
+ case '{':
+ try {
+ return new JSONObject(this, config);
+ } catch (final StackOverflowError e) {
+ throw new JSONException("JSONObject depth too large to process.", e);
+ }
+ case '[':
+ try {
+ return new JSONArray(this, config);
+ } catch (final StackOverflowError e) {
+ throw new JSONException("JSONObject depth too large to process.", e);
+ }
+ }
+ throw new JSONException("Unsupported object build for token {}", token);
+ });
+ }
+
+ /**
+ * 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
+ *
+ * @param getOnlyStringValue 是否只获取String值
+ * @param objectBuilder JSON对象构建器
+ * @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
+ * @throws JSONException 语法错误
+ */
+ public Object nextValue(final boolean getOnlyStringValue, final ObjectBuilder objectBuilder) throws JSONException {
+ final JSONTokener tokener = this.tokener;
+ char c = tokener.nextClean();
+ switch (c) {
+ case '"':
+ case '\'':
+ return tokener.nextString(c);
+ case '{':
+ case '[':
+ if (getOnlyStringValue) {
+ throw tokener.syntaxError("String value must not begin with '{'");
+ }
+ tokener.back();
+ return objectBuilder.build(c, tokener, this.config);
+ }
+
+ /*
+ * Handle unquoted text. This could be the values true, false, or null, or it can be a number.
+ * An implementation (such as this one) is allowed to also accept non-standard forms. Accumulate
+ * characters until we reach the end of the text or a formatting character.
+ */
+
+ final StringBuilder sb = new StringBuilder();
+ while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
+ sb.append(c);
+ c = tokener.next();
+ }
+ tokener.back();
+
+ final String valueString = sb.toString().trim();
+ if (valueString.isEmpty()) {
+ throw tokener.syntaxError("Missing value");
+ }
+ return getOnlyStringValue ? valueString : InternalJSONUtil.parseValueFromString(valueString);
+ }
+
+ /**
+ * 对象构建抽象,通过实现此接口,从{@link JSONTokener}解析值并构建指定对象
+ */
+ @FunctionalInterface
+ public interface ObjectBuilder {
+ /**
+ * 构建
+ *
+ * @param token 符号表示,用于区分对象类型
+ * @param tokener {@link JSONTokener}
+ * @param config {@link JSONConfig}
+ * @return 构建的对象
+ */
+ Object build(char token, JSONTokener tokener, JSONConfig config);
+ }
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java b/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java
index a2692366c..a29af097a 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/JSONTokener.java
@@ -29,6 +29,11 @@ import java.io.StringReader;
*/
public class JSONTokener extends ReaderWrapper {
+ /**
+ * 定义结束(End of stream)为:0
+ */
+ public static final int EOF = 0;
+
private long character;
/**
* 是否结尾 End of stream
@@ -51,41 +56,33 @@ public class JSONTokener extends ReaderWrapper {
*/
private boolean usePrevious;
- /**
- * JSON配置
- */
- private final JSONConfig config;
-
// ------------------------------------------------------------------------------------ Constructor start
/**
* 从InputStream中构建,使用UTF-8编码
*
* @param inputStream InputStream
- * @param config JSON配置
* @throws JSONException JSON异常,包装IO异常
*/
- public JSONTokener(final InputStream inputStream, final JSONConfig config) throws JSONException {
- this(IoUtil.toUtf8Reader(inputStream), config);
+ public JSONTokener(final InputStream inputStream) throws JSONException {
+ this(IoUtil.toUtf8Reader(inputStream));
}
/**
* 从字符串中构建
*
- * @param s JSON字符串
- * @param config JSON配置
+ * @param s JSON字符串
*/
- public JSONTokener(final CharSequence s, final JSONConfig config) {
- this(new StringReader(Assert.notBlank(s).toString()), config);
+ public JSONTokener(final CharSequence s) {
+ this(new StringReader(Assert.notBlank(s).toString()));
}
/**
* 从Reader中构建
*
* @param reader Reader
- * @param config JSON配置
*/
- public JSONTokener(final Reader reader, final JSONConfig config) {
+ public JSONTokener(final Reader reader) {
super(IoUtil.toMarkSupport(Assert.notNull(reader)));
this.eof = false;
this.usePrevious = false;
@@ -93,7 +90,6 @@ public class JSONTokener extends ReaderWrapper {
this.index = 0;
this.character = 1;
this.line = 1;
- this.config = config;
}
// ------------------------------------------------------------------------------------ Constructor end
@@ -152,9 +148,9 @@ public class JSONTokener extends ReaderWrapper {
throw new JSONException(exception);
}
- if (c <= 0) { // End of stream
+ if (c <= EOF) { // End of stream
this.eof = true;
- c = 0;
+ c = EOF;
}
}
this.index += 1;
@@ -183,9 +179,10 @@ public class JSONTokener extends ReaderWrapper {
/**
* 获取16进制unicode转义符对应的字符值,如:
*
{@code '4f60' -> '你'}
+ *
* @return 字符
*/
- public char nextUnicode(){
+ public char nextUnicode() {
return (char) NumberUtil.parseInt(next(4), 16);
}
@@ -332,61 +329,6 @@ public class JSONTokener extends ReaderWrapper {
}
}
- /**
- * 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
- *
- * @param getOnlyStringValue 是否只获取String值
- * @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
- * @throws JSONException 语法错误
- */
- public Object nextValue(final boolean getOnlyStringValue) throws JSONException {
- char c = this.nextClean();
- switch (c) {
- case '"':
- case '\'':
- return this.nextString(c);
- case '{':
- if (getOnlyStringValue) {
- throw this.syntaxError("String value must not begin with '{'");
- }
- this.back();
- try {
- return new JSONObject(this, this.config);
- } catch (final StackOverflowError e) {
- throw new JSONException("JSONObject depth too large to process.", e);
- }
- case '[':
- if (getOnlyStringValue) {
- throw this.syntaxError("String value must not begin with '['");
- }
- this.back();
- try {
- return new JSONArray(this, this.config);
- } catch (final StackOverflowError e) {
- throw new JSONException("JSONArray depth too large to process.", e);
- }
- }
-
- /*
- * Handle unquoted text. This could be the values true, false, or null, or it can be a number.
- * An implementation (such as this one) is allowed to also accept non-standard forms. Accumulate
- * characters until we reach the end of the text or a formatting character.
- */
-
- final StringBuilder sb = new StringBuilder();
- while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
- sb.append(c);
- c = this.next();
- }
- this.back();
-
- final String valueString = sb.toString().trim();
- if (valueString.isEmpty()) {
- throw this.syntaxError("Missing value");
- }
- return getOnlyStringValue ? valueString : InternalJSONUtil.parseValueFromString(valueString);
- }
-
/**
* Skip characters until the next character is the requested character. If the requested character is not found, no characters are skipped. 在遇到指定字符前,跳过其它字符。如果字符未找到,则不跳过任何字符。
*
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java
index 7ce043539..ff4a73786 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/convert/JSONConverter.java
@@ -103,14 +103,14 @@ public class JSONConverter implements Converter, Serializable {
// 对象转JSON
final Class> targetClass = TypeUtil.getClass(targetType);
- if(null != targetClass){
+ if (null != targetClass) {
if (JSON.class.isAssignableFrom(targetClass)) {
return toJSON(value);
}
// 自定义日期格式
- if(Date.class.isAssignableFrom(targetClass) || TemporalAccessor.class.isAssignableFrom(targetClass)){
+ if (Date.class.isAssignableFrom(targetClass) || TemporalAccessor.class.isAssignableFrom(targetClass)) {
final Object date = toDateWithFormat(targetClass, value);
- if(null != date){
+ if (null != date) {
return date;
}
}
@@ -120,7 +120,7 @@ public class JSONConverter implements Converter, Serializable {
}
/**
- * 实现Object对象转换为{@link JSON},支持的对象:
+ * 实现Object对象转换为JSON对象,根据RFC8259规范,支持的对象:
*
* - String: 转换为相应的对象,"和'包围的字符串返回原字符串,""返回{@code null}
* - Array、Iterable、Iterator:转换为JSONArray
@@ -133,15 +133,14 @@ public class JSONConverter implements Converter, Serializable {
* @return 转换后的对象
* @throws JSONException 转换异常
*/
- @SuppressWarnings("resource")
public Object toJSON(Object obj) throws JSONException {
- if(null == obj){
+ if (null == obj) {
return null;
}
- if(obj instanceof Optional){
+ if (obj instanceof Optional) {
obj = ((Optional>) obj).orElse(null);
- } else if(obj instanceof Opt){
+ } else if (obj instanceof Opt) {
obj = ((Opt>) obj).get();
}
@@ -149,26 +148,7 @@ public class JSONConverter implements Converter, Serializable {
if (obj instanceof JSON || obj instanceof Number || obj instanceof Boolean) {
return obj;
} else if (obj instanceof CharSequence) {
- final String jsonStr = StrUtil.trim((CharSequence) obj);
- if(jsonStr.isEmpty()){
- // 空按照null值处理
- return null;
- }
- final char firstC = jsonStr.charAt(0);
- switch (firstC){
- case '[':
- return new JSONArray(jsonStr, config);
- case '{':
- return new JSONObject(jsonStr, config);
- default:
- // RFC8259,JSON字符串值、number, boolean, or null
- final Object value = new JSONTokener(jsonStr, config).nextValue(false);
- if(ObjUtil.equals(value, jsonStr)){
- // 非可解析的字符串,原样返回
- return InternalJSONUtil.quote((CharSequence) value);
- }
- return value;
- }
+ return toJSON((CharSequence) obj);
} else if (obj instanceof MapWrapper) {
// MapWrapper实现了Iterable会被当作JSONArray,此处做修正
json = new JSONObject(obj, config);
@@ -181,6 +161,46 @@ public class JSONConverter implements Converter, Serializable {
return json;
}
+ /**
+ * 实现{@link CharSequence}转换为JSON对象,根据RFC8259规范
+ * 转换为相应的对象,"和'包围的字符串返回原字符串,""返回{@code null}
+ *
+ * @param str 被转换的字符串
+ * @return 转换后的对象
+ * @throws JSONException 转换异常
+ */
+ public Object toJSON(final CharSequence str) throws JSONException {
+ if (null == str) {
+ return null;
+ }
+
+ final String jsonStr = StrUtil.trim(str);
+ if (jsonStr.isEmpty()) {
+ // https://www.rfc-editor.org/rfc/rfc8259#section-7
+ // 未被包装的空串理解为null
+ return null;
+ }
+ final char firstC = jsonStr.charAt(0);
+ // RFC8259,JSON字符串值、number, boolean, or null
+ final JSONParser jsonParser = JSONParser.of(new JSONTokener(jsonStr), config);
+ final Object value = jsonParser.nextValue(false);
+ if(jsonParser.getTokener().nextClean() != JSONTokener.EOF){
+ // 对于用户提供的未转义字符串导致解析未结束,报错
+ throw new JSONException("JSON format error: {}", jsonStr);
+ }
+ switch (firstC) {
+ case '"':
+ case '\'':
+ return InternalJSONUtil.quote((CharSequence) value);
+ default:
+ if(ObjUtil.equals(jsonStr, value)){
+ // 对于直接的字符串,如abc,按照字符串处理
+ return InternalJSONUtil.quote((CharSequence) value);
+ }
+ return value;
+ }
+ }
+
// ----------------------------------------------------------- Private method start
/**
@@ -221,13 +241,13 @@ public class JSONConverter implements Converter, Serializable {
// 尝试转Bean
if (BeanUtil.isWritableBean(rawType)) {
// issue#I5WDP0 对于Kotlin对象,由于参数可能非空限制,导致无法创建一个默认的对象再赋值
- if(KClassUtil.isKotlinClass(rawType) && json instanceof JSONGetter){
- return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter)json));
+ if (KClassUtil.isKotlinClass(rawType) && json instanceof JSONGetter) {
+ return KClassUtil.newInstance(rawType, new JSONGetterValueProvider<>((JSONGetter) json));
}
return BeanCopier.of(json,
- ConstructorUtil.newInstanceIfPossible(rawType), targetType,
- InternalJSONUtil.toCopyOptions(json.config())).copy();
+ ConstructorUtil.newInstanceIfPossible(rawType), targetType,
+ InternalJSONUtil.toCopyOptions(json.config())).copy();
}
// 跳过异常时返回null
@@ -237,7 +257,7 @@ public class JSONConverter implements Converter, Serializable {
// 无法转换
throw new JSONException("Can not convert from '{}': {} to '{}'",
- json.getClass().getName(), json, targetType.getTypeName());
+ json.getClass().getName(), json, targetType.getTypeName());
}
/**
@@ -264,7 +284,7 @@ public class JSONConverter implements Converter, Serializable {
}
// 日期、java.sql中的日期以及自定义日期统一处理
- if(Date.class.isAssignableFrom(rowType)){
+ if (Date.class.isAssignableFrom(rowType)) {
return (T) DateConverter.INSTANCE.convert(type, value);
}
@@ -279,7 +299,7 @@ public class JSONConverter implements Converter, Serializable {
}
// issue#I6SZYB Entry类(含有泛型参数,不可以默认强转)
- if(Map.Entry.class.isAssignableFrom(rowType)){
+ if (Map.Entry.class.isAssignableFrom(rowType)) {
return (T) EntryConverter.INSTANCE.convert(type, value);
}
@@ -294,12 +314,12 @@ public class JSONConverter implements Converter, Serializable {
}
// Record
- if(RecordUtil.isRecord(rowType)){
+ if (RecordUtil.isRecord(rowType)) {
return (T) RecordConverter.INSTANCE.convert(type, value);
}
// 空值转空Bean
- if(ObjUtil.isEmpty(value)){
+ if (ObjUtil.isEmpty(value)) {
// issue#3649 空值转空对象,则直接实例化
return ConstructorUtil.newInstanceIfPossible(rowType);
}
@@ -308,7 +328,7 @@ public class JSONConverter implements Converter, Serializable {
return null;
}
- private Object toDateWithFormat(final Class> targetClass, final Object value){
+ private Object toDateWithFormat(final Class> targetClass, final Object value) {
// 日期转换,支持自定义日期格式
final String format = config.getDateFormat();
if (StrUtil.isNotBlank(format)) {
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java
index 31a73d996..9f10fbedc 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONArrayMapper.java
@@ -17,10 +17,7 @@ import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.mutable.Mutable;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.array.ArrayUtil;
-import org.dromara.hutool.json.JSONArray;
-import org.dromara.hutool.json.JSONException;
-import org.dromara.hutool.json.JSONParser;
-import org.dromara.hutool.json.JSONTokener;
+import org.dromara.hutool.json.*;
import org.dromara.hutool.json.serialize.GlobalSerializeMapping;
import org.dromara.hutool.json.serialize.JSONSerializer;
@@ -91,18 +88,20 @@ public class JSONArrayMapper {
}
if (source instanceof JSONTokener) {
- mapFromTokener((JSONTokener) source, jsonArray);
- }else if (source instanceof CharSequence) {
+ mapFromTokener((JSONTokener) source, JSONConfig.of(), jsonArray);
+ }if (source instanceof JSONParser) {
+ ((JSONParser)source).parseTo(jsonArray, this.predicate);
+ } else if (source instanceof CharSequence) {
// JSON字符串
mapFromStr((CharSequence) source, jsonArray);
} else if (source instanceof Reader) {
- mapFromTokener(new JSONTokener((Reader) source, jsonArray.config()), jsonArray);
+ mapFromTokener(new JSONTokener((Reader) source), jsonArray.config(), jsonArray);
} else if (source instanceof InputStream) {
- mapFromTokener(new JSONTokener((InputStream) source, jsonArray.config()), jsonArray);
+ mapFromTokener(new JSONTokener((InputStream) source), jsonArray.config(), jsonArray);
} else if (source instanceof byte[]) {
final byte[] bytesSource = (byte[]) source;
if ('[' == bytesSource[0] && ']' == bytesSource[bytesSource.length - 1]) {
- mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource), jsonArray.config()), jsonArray);
+ mapFromTokener(new JSONTokener(IoUtil.toStream(bytesSource)), jsonArray.config(), jsonArray);
} else {
// https://github.com/dromara/hutool/issues/2369
// 非标准的二进制流,则按照普通数组对待
@@ -119,8 +118,8 @@ public class JSONArrayMapper {
} else if (source instanceof Iterable>) {// Iterable
iter = ((Iterable>) source).iterator();
} else {
- if(!jsonArray.config().isIgnoreError()){
- throw new JSONException("JSONArray initial value should be a string or collection or array.");
+ if (!jsonArray.config().isIgnoreError()) {
+ throw new JSONException("Unsupported [{}] to JSONArray", source.getClass());
}
// 如果用户选择跳过异常,则跳过此值转换
return;
@@ -145,7 +144,7 @@ public class JSONArrayMapper {
*/
private void mapFromStr(final CharSequence source, final JSONArray jsonArray) {
if (null != source) {
- mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonArray.config()), jsonArray);
+ mapFromTokener(new JSONTokener(StrUtil.trim(source)), jsonArray.config(), jsonArray);
}
}
@@ -155,7 +154,7 @@ public class JSONArrayMapper {
* @param x {@link JSONTokener}
* @param jsonArray {@link JSONArray}
*/
- private void mapFromTokener(final JSONTokener x, final JSONArray jsonArray) {
- JSONParser.of(x).parseTo(jsonArray, this.predicate);
+ private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONArray jsonArray) {
+ JSONParser.of(x, config).parseTo(jsonArray, this.predicate);
}
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java
index 4f9cb4842..13c44752c 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/mapper/JSONObjectMapper.java
@@ -103,7 +103,10 @@ public class JSONObjectMapper {
if (source instanceof JSONTokener) {
// JSONTokener
- mapFromTokener((JSONTokener) source, jsonObject);
+ mapFromTokener((JSONTokener) source, jsonObject.config(), jsonObject);
+ }if (source instanceof JSONParser) {
+ // JSONParser
+ ((JSONParser) source).parseTo(jsonObject, this.predicate);
} else if (source instanceof Map) {
// Map
for (final Map.Entry, ?> e : ((Map, ?>) source).entrySet()) {
@@ -116,11 +119,11 @@ public class JSONObjectMapper {
// 可能为JSON字符串
mapFromStr((CharSequence) source, jsonObject);
} else if (source instanceof Reader) {
- mapFromTokener(new JSONTokener((Reader) source, jsonObject.config()), jsonObject);
+ mapFromTokener(new JSONTokener((Reader) source), jsonObject.config(), jsonObject);
} else if (source instanceof InputStream) {
- mapFromTokener(new JSONTokener((InputStream) source, jsonObject.config()), jsonObject);
+ mapFromTokener(new JSONTokener((InputStream) source), jsonObject.config(), jsonObject);
} else if (source instanceof byte[]) {
- mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source), jsonObject.config()), jsonObject);
+ mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source)), jsonObject.config(), jsonObject);
} else if (source instanceof ResourceBundle) {
// ResourceBundle
mapFromResourceBundle((ResourceBundle) source, jsonObject);
@@ -170,17 +173,18 @@ public class JSONObjectMapper {
JSONXMLParser.of(ParseConfig.of(), this.predicate).parseJSONObject(jsonStr, jsonObject);
return;
}
- mapFromTokener(new JSONTokener(StrUtil.trim(source), jsonObject.config()), jsonObject);
+ mapFromTokener(new JSONTokener(StrUtil.trim(source)), jsonObject.config(), jsonObject);
}
/**
* 从{@link JSONTokener}转换
*
* @param x JSONTokener
+ * @param config JSON配置
* @param jsonObject {@link JSONObject}
*/
- private void mapFromTokener(final JSONTokener x, final JSONObject jsonObject) {
- JSONParser.of(x).parseTo(jsonObject, this.predicate);
+ private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONObject jsonObject) {
+ JSONParser.of(x, config).parseTo(jsonObject, this.predicate);
}
/**
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java b/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java
index d08f36317..f4826cf5d 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/xml/JSONXMLParser.java
@@ -64,7 +64,7 @@ public class JSONXMLParser {
* @throws JSONException 解析异常
*/
public void parseJSONObject(final String xmlStr, final JSONObject jo) throws JSONException {
- final XMLTokener x = new XMLTokener(xmlStr, jo.config());
+ final XMLTokener x = new XMLTokener(xmlStr);
while (x.more() && x.skipPast("<")) {
parse(x, jo, null, 0);
}
diff --git a/hutool-json/src/main/java/org/dromara/hutool/json/xml/XMLTokener.java b/hutool-json/src/main/java/org/dromara/hutool/json/xml/XMLTokener.java
index d3eb34bb8..3e83f71d1 100644
--- a/hutool-json/src/main/java/org/dromara/hutool/json/xml/XMLTokener.java
+++ b/hutool-json/src/main/java/org/dromara/hutool/json/xml/XMLTokener.java
@@ -44,10 +44,9 @@ public class XMLTokener extends JSONTokener {
* Construct an XMLTokener from a string.
*
* @param s A source string.
- * @param config JSON配置
*/
- public XMLTokener(final CharSequence s, final JSONConfig config) {
- super(s, config);
+ public XMLTokener(final CharSequence s) {
+ super(s);
}
/**
diff --git a/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java b/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java
index 74b73de53..dfd530523 100644
--- a/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java
+++ b/hutool-json/src/test/java/org/dromara/hutool/json/JSONUtilTest.java
@@ -12,6 +12,7 @@
package org.dromara.hutool.json;
+import lombok.Data;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.map.MapUtil;
@@ -20,48 +21,68 @@ import org.dromara.hutool.json.serialize.JSONStringer;
import org.dromara.hutool.json.test.bean.Price;
import org.dromara.hutool.json.test.bean.UserA;
import org.dromara.hutool.json.test.bean.UserC;
-import lombok.Data;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.sql.SQLException;
import java.util.*;
+import static org.junit.jupiter.api.Assertions.*;
+
public class JSONUtilTest {
@Test
public void parseInvalid() {
- Assertions.assertThrows(JSONException.class, ()->{
+ assertThrows(JSONException.class, () -> {
JSONUtil.parse("'abc");
});
}
@Test
public void parseInvalid3() {
- Assertions.assertThrows(JSONException.class, ()->{
+ assertThrows(JSONException.class, () -> {
JSONUtil.parse("\"abc");
});
}
+ @Test
+ void parseEmptyValue() {
+ // https://www.rfc-editor.org/rfc/rfc8259#section-7
+ // 未被包装的空串理解为null
+ Object parse = JSONUtil.parse("");
+ assertNull(parse);
+
+ parse = JSONUtil.parse("\"\"");
+ assertEquals("\"\"", parse);
+ }
+
@Test
public void parseValueTest() {
Object parse = JSONUtil.parse(123);
- Assertions.assertEquals(123, parse);
+ assertEquals(123, parse);
parse = JSONUtil.parse("\"abc\"");
- Assertions.assertEquals("abc", parse);
+ assertEquals("\"abc\"", parse);
+
+ parse = JSONUtil.parse("\"\\\"bc\"");
+ assertEquals("\"\\\"bc\"", parse);
parse = JSONUtil.parse("true");
- Assertions.assertEquals(true, parse);
+ assertEquals(true, parse);
parse = JSONUtil.parse("False");
- Assertions.assertEquals(false, parse);
+ assertEquals(false, parse);
parse = JSONUtil.parse("null");
- Assertions.assertNull(parse);
+ assertNull(parse);
+ }
- parse = JSONUtil.parse("");
- Assertions.assertNull(parse);
+ @Test
+ void parseInvalidTest() {
+ assertThrows(JSONException.class, () -> {
+ // 包装符需要转义,此处未转义报错
+ JSONUtil.parse("\"a\"bc\"");
+ });
}
/**
@@ -69,7 +90,7 @@ public class JSONUtilTest {
*/
@Test
public void parseTest() {
- Assertions.assertThrows(JSONException.class, ()->{
+ assertThrows(JSONException.class, () -> {
JSONUtil.parseArray("[{\"a\":\"a\\x]");
});
}
@@ -79,7 +100,7 @@ public class JSONUtilTest {
*/
@Test
public void parseNumberToJSONArrayTest() {
- Assertions.assertThrows(JSONException.class, ()->{
+ assertThrows(JSONException.class, () -> {
final JSONArray json = JSONUtil.parseArray(123L);
Assertions.assertNotNull(json);
});
@@ -91,7 +112,7 @@ public class JSONUtilTest {
@Test
public void parseNumberToJSONArrayTest2() {
final JSONArray json = JSONUtil.parseArray(123L,
- JSONConfig.of().setIgnoreError(true));
+ JSONConfig.of().setIgnoreError(true));
Assertions.assertNotNull(json);
}
@@ -100,7 +121,7 @@ public class JSONUtilTest {
*/
@Test
public void parseNumberToJSONObjectTest() {
- Assertions.assertThrows(JSONException.class, ()->{
+ assertThrows(JSONException.class, () -> {
final JSONObject json = JSONUtil.parseObj(123L);
Assertions.assertNotNull(json);
});
@@ -112,7 +133,7 @@ public class JSONUtilTest {
@Test
public void parseNumberToJSONObjectTest2() {
final JSONObject json = JSONUtil.parseObj(123L, JSONConfig.of().setIgnoreError(true));
- Assertions.assertEquals(new JSONObject(), json);
+ assertEquals(new JSONObject(), json);
}
@Test
@@ -149,8 +170,8 @@ public class JSONUtilTest {
final JSONObject jsonObject = JSONUtil.parseObj(data);
Assertions.assertTrue(jsonObject.containsKey("model"));
- Assertions.assertEquals(1, jsonObject.getJSONObject("model").getInt("type").intValue());
- Assertions.assertEquals("17610836523", jsonObject.getJSONObject("model").getStr("mobile"));
+ assertEquals(1, jsonObject.getJSONObject("model").getInt("type").intValue());
+ assertEquals("17610836523", jsonObject.getJSONObject("model").getStr("mobile"));
// Assertions.assertEquals("{\"model\":{\"type\":1,\"mobile\":\"17610836523\"}}", jsonObject.toString());
}
@@ -166,11 +187,11 @@ public class JSONUtilTest {
map.put("user", object.toString());
final JSONObject json = JSONUtil.parseObj(map);
- Assertions.assertEquals("{\"name\":\"123123\",\"value\":\"\\\\\",\"value2\":\"\"}", json.get("user"));
- Assertions.assertEquals("{\"user\":\"{\\\"name\\\":\\\"123123\\\",\\\"value\\\":\\\"\\\\\\\\\\\",\\\"value2\\\":\\\"\\\"}\"}", json.toString());
+ assertEquals("{\"name\":\"123123\",\"value\":\"\\\\\",\"value2\":\"\"}", json.get("user"));
+ assertEquals("{\"user\":\"{\\\"name\\\":\\\"123123\\\",\\\"value\\\":\\\"\\\\\\\\\\\",\\\"value2\\\":\\\"\\\"}\"}", json.toString());
final JSONObject json2 = JSONUtil.parseObj(json.toString());
- Assertions.assertEquals("{\"name\":\"123123\",\"value\":\"\\\\\",\"value2\":\"\"}", json2.get("user"));
+ assertEquals("{\"name\":\"123123\",\"value\":\"\\\\\",\"value2\":\"\"}", json2.get("user"));
}
@Test
@@ -180,12 +201,13 @@ public class JSONUtilTest {
private static final long serialVersionUID = 1L;
{
- put("attributes", "a");
- put("b", "b");
- put("c", "c");
- }};
+ put("attributes", "a");
+ put("b", "b");
+ put("c", "c");
+ }
+ };
- Assertions.assertEquals("{\"attributes\":\"a\",\"b\":\"b\",\"c\":\"c\"}", JSONUtil.toJsonStr(sortedMap));
+ assertEquals("{\"attributes\":\"a\",\"b\":\"b\",\"c\":\"c\"}", JSONUtil.toJsonStr(sortedMap));
}
/**
@@ -196,7 +218,7 @@ public class JSONUtilTest {
final String json = "{\"ADT\":[[{\"BookingCode\":[\"N\",\"N\"]}]]}";
final Price price = JSONUtil.toBean(json, Price.class);
- Assertions.assertEquals("N", price.getADT().get(0).get(0).getBookingCode().get(0));
+ assertEquals("N", price.getADT().get(0).get(0).getBookingCode().get(0));
}
@Test
@@ -207,8 +229,8 @@ public class JSONUtilTest {
Assertions.assertNotNull(user.getProp());
final String prop = user.getProp();
final JSONObject propJson = JSONUtil.parseObj(prop);
- Assertions.assertEquals("男", propJson.getStr("gender"));
- Assertions.assertEquals(18, propJson.getInt("age").intValue());
+ assertEquals("男", propJson.getStr("gender"));
+ assertEquals(18, propJson.getInt("age").intValue());
// Assertions.assertEquals("{\"age\":18,\"gender\":\"男\"}", user.getProp());
}
@@ -216,24 +238,24 @@ public class JSONUtilTest {
public void getStrTest() {
final String html = "{\"name\":\"Something must have been changed since you leave\"}";
final JSONObject jsonObject = JSONUtil.parseObj(html);
- Assertions.assertEquals("Something must have been changed since you leave", jsonObject.getStr("name"));
+ assertEquals("Something must have been changed since you leave", jsonObject.getStr("name"));
}
@Test
public void getStrTest2() {
final String html = "{\"name\":\"Something\\u00a0must have been changed since you leave\"}";
final JSONObject jsonObject = JSONUtil.parseObj(html);
- Assertions.assertEquals("Something\\u00a0must\\u00a0have\\u00a0been\\u00a0changed\\u00a0since\\u00a0you\\u00a0leave", jsonObject.getStrEscaped("name"));
+ assertEquals("Something\\u00a0must\\u00a0have\\u00a0been\\u00a0changed\\u00a0since\\u00a0you\\u00a0leave", jsonObject.getStrEscaped("name"));
}
@Test
public void parseFromXmlTest() {
final String s = "640102197312070614640102197312070614Xaa1";
final JSONObject json = JSONUtil.parseFromXml(s);
- Assertions.assertEquals(640102197312070614L, json.get("sfzh"));
- Assertions.assertEquals("640102197312070614X", json.get("sfz"));
- Assertions.assertEquals("aa", json.get("name"));
- Assertions.assertEquals(1, json.get("gender"));
+ assertEquals(640102197312070614L, json.get("sfzh"));
+ assertEquals("640102197312070614X", json.get("sfz"));
+ assertEquals("aa", json.get("name"));
+ assertEquals(1, json.get("gender"));
}
@Test
@@ -241,81 +263,81 @@ public class JSONUtilTest {
final String json = "{\"test\": 12.00}";
final JSONObject jsonObject = JSONUtil.parseObj(json);
//noinspection BigDecimalMethodWithoutRoundingCalled
- Assertions.assertEquals("12.00", jsonObject.getBigDecimal("test").setScale(2).toString());
+ assertEquals("12.00", jsonObject.getBigDecimal("test").setScale(2).toString());
}
@Test
public void customValueTest() {
final JSONObject jsonObject = JSONUtil.ofObj()
- .set("test2", (JSONStringer) () -> NumberUtil.format("#.0", 12.00D));
+ .set("test2", (JSONStringer) () -> NumberUtil.format("#.0", 12.00D));
- Assertions.assertEquals("{\"test2\":12.0}", jsonObject.toString());
+ assertEquals("{\"test2\":12.0}", jsonObject.toString());
}
@Test
public void setStripTrailingZerosTest() {
// 默认去除多余的0
final JSONObject jsonObjectDefault = JSONUtil.ofObj()
- .set("test2", 12.00D);
- Assertions.assertEquals("{\"test2\":12}", jsonObjectDefault.toString());
+ .set("test2", 12.00D);
+ assertEquals("{\"test2\":12}", jsonObjectDefault.toString());
// 不去除多余的0
final JSONObject jsonObject = JSONUtil.ofObj(JSONConfig.of().setStripTrailingZeros(false))
- .set("test2", 12.00D);
- Assertions.assertEquals("{\"test2\":12.0}", jsonObject.toString());
+ .set("test2", 12.00D);
+ assertEquals("{\"test2\":12.0}", jsonObject.toString());
// 去除多余的0
jsonObject.config().setStripTrailingZeros(true);
- Assertions.assertEquals("{\"test2\":12}", jsonObject.toString());
+ assertEquals("{\"test2\":12}", jsonObject.toString());
}
@Test
public void parseObjTest() {
// 测试转义
final JSONObject jsonObject = JSONUtil.parseObj("{\n" +
- " \"test\": \"\\\\地库地库\",\n" +
- "}");
+ " \"test\": \"\\\\地库地库\",\n" +
+ "}");
- Assertions.assertEquals("\\地库地库", jsonObject.getObj("test"));
+ assertEquals("\\地库地库", jsonObject.getObj("test"));
}
@Test
- public void sqlExceptionTest(){
+ public void sqlExceptionTest() {
//https://github.com/dromara/hutool/issues/1399
// SQLException实现了Iterable接口,默认是遍历之,会栈溢出,修正后只返回string
final JSONObject set = JSONUtil.ofObj().set("test", new SQLException("test"));
- Assertions.assertEquals("{\"test\":\"java.sql.SQLException: test\"}", set.toString());
+ assertEquals("{\"test\":\"java.sql.SQLException: test\"}", set.toString());
}
@Test
- public void parseBigNumberTest(){
+ public void parseBigNumberTest() {
// 科学计数法使用BigDecimal处理,默认输出非科学计数形式
final String str = "{\"test\":100000054128897953e4}";
- Assertions.assertEquals("{\"test\":1000000541288979530000}", JSONUtil.parseObj(str).toString());
+ assertEquals("{\"test\":1000000541288979530000}", JSONUtil.parseObj(str).toString());
}
@Test
- public void toXmlTest(){
+ public void toXmlTest() {
final JSONObject obj = JSONUtil.ofObj();
obj.set("key1", "v1")
- .set("key2", ListUtil.view("a", "b", "c"));
+ .set("key2", ListUtil.view("a", "b", "c"));
final String xmlStr = JSONUtil.toXmlStr(obj);
- Assertions.assertEquals("v1abc", xmlStr);
+ assertEquals("v1abc", xmlStr);
}
@Test
- public void toJsonStrOfStringTest(){
+ public void toJsonStrOfStringTest() {
final String a = "a";
final String s = JSONUtil.toJsonStr(a);
- Assertions.assertEquals("\"a\"", s);
+ assertEquals("\"a\"", s);
}
@Test
- public void toJsonStrOfNumberTest(){
+ public void toJsonStrOfNumberTest() {
final int a = 1;
final String s = JSONUtil.toJsonStr(a);
- Assertions.assertEquals("1", s);
+ assertEquals("1", s);
}
/**
@@ -325,7 +347,7 @@ public class JSONUtilTest {
public void testArrayEntity() {
final String jsonStr = JSONUtil.toJsonStr(new ArrayEntity());
// a为空的bytes数组,按照空的流对待
- Assertions.assertEquals("{\"b\":[0],\"c\":[],\"d\":[],\"e\":[]}", jsonStr);
+ assertEquals("{\"b\":[0],\"c\":[],\"d\":[],\"e\":[]}", jsonStr);
}
@Data
@@ -340,13 +362,13 @@ public class JSONUtilTest {
@Test
void toJsonStrOfBooleanTest() {
final String jsonStr = JSONUtil.toJsonStr(true);
- Assertions.assertEquals("true", jsonStr);
+ assertEquals("true", jsonStr);
}
@Test
public void issue3540Test() {
- final Long userId=10101010L;
+ final Long userId = 10101010L;
final String jsonStr = JSONUtil.toJsonStr(userId);
- Assertions.assertEquals("10101010", jsonStr);
+ assertEquals("10101010", jsonStr);
}
}