This commit is contained in:
Looly
2023-03-26 08:53:56 +08:00
parent 1d4d4e0e76
commit f970dc7632
25 changed files with 308 additions and 98 deletions

View File

@@ -21,9 +21,7 @@ import cn.hutool.json.serialize.JSONStringer;
import cn.hutool.json.writer.GlobalValueWriterMapping;
import cn.hutool.json.writer.JSONValueWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.SQLException;
@@ -316,7 +314,7 @@ public final class InternalJSONUtil {
*/
public static Writer quote(final String str, final Writer writer, final boolean isWrap) throws IORuntimeException {
try {
return doQuote(str, writer, isWrap);
return _quote(str, writer, isWrap);
} catch (final IOException e) {
throw new IORuntimeException(e);
}
@@ -422,7 +420,7 @@ public final class InternalJSONUtil {
* @throws IOException IO异常
* @since 3.3.1
*/
private static Writer doQuote(final String str, final Writer writer, final boolean isWrap) throws IOException {
private static Writer _quote(final String str, final Writer writer, final boolean isWrap) throws IOException {
if (StrUtil.isEmpty(str)) {
if (isWrap) {
writer.write("\"\"");

View File

@@ -8,7 +8,7 @@ import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.text.StrJoiner;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.mapper.ArrayMapper;
import cn.hutool.json.mapper.JSONArrayMapper;
import cn.hutool.json.writer.JSONWriter;
import java.io.StringWriter;
@@ -148,7 +148,7 @@ public class JSONArray implements JSON, JSONGetter<Integer>, List<Object>, Rando
*/
public JSONArray(final Object object, final JSONConfig jsonConfig, final Predicate<Mutable<Object>> predicate) throws JSONException {
this(DEFAULT_CAPACITY, jsonConfig);
ArrayMapper.of(object, predicate).mapTo(this);
JSONArrayMapper.of(object, predicate).mapTo(this);
}
// endregion

View File

@@ -9,7 +9,7 @@ import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.map.MapWrapper;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.mapper.ObjectMapper;
import cn.hutool.json.mapper.JSONObjectMapper;
import cn.hutool.json.writer.JSONWriter;
import java.io.StringWriter;
@@ -132,7 +132,7 @@ public class JSONObject extends MapWrapper<String, Object> implements JSON, JSON
*/
public JSONObject(final Object source, final JSONConfig config, final Predicate<MutableEntry<String, Object>> predicate) {
this(DEFAULT_CAPACITY, config);
ObjectMapper.of(source, predicate).mapTo(this);
JSONObjectMapper.of(source, predicate).mapTo(this);
}
// -------------------------------------------------------------------------------------------------------------------- Constructor end

View File

@@ -1,9 +1,9 @@
package cn.hutool.json;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.ReaderWrapper;
import cn.hutool.core.text.StrUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
@@ -14,7 +14,7 @@ import java.io.StringReader;
*
* @author from JSON.org
*/
public class JSONTokener {
public class JSONTokener extends ReaderWrapper {
private long character;
/**
@@ -37,10 +37,6 @@ public class JSONTokener {
* 是否使用前一个字符
*/
private boolean usePrevious;
/**
* 源
*/
private final Reader reader;
/**
* JSON配置
@@ -48,24 +44,6 @@ public class JSONTokener {
private final JSONConfig config;
// ------------------------------------------------------------------------------------ Constructor start
/**
* 从Reader中构建
*
* @param reader Reader
* @param config JSON配置
*/
public JSONTokener(final Reader reader, final JSONConfig config) {
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.line = 1;
this.config = config;
}
/**
* 从InputStream中构建使用UTF-8编码
*
@@ -86,6 +64,23 @@ public class JSONTokener {
public JSONTokener(final CharSequence s, final JSONConfig config) {
this(new StringReader(StrUtil.str(s)), config);
}
/**
* 从Reader中构建
*
* @param reader Reader
* @param config JSON配置
*/
public JSONTokener(final Reader reader, final JSONConfig config) {
super(IoUtil.toMarkSupport(reader));
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.line = 1;
this.config = config;
}
// ------------------------------------------------------------------------------------ Constructor end
/**
@@ -138,7 +133,7 @@ public class JSONTokener {
c = this.previous;
} else {
try {
c = this.reader.read();
c = read();
} catch (final IOException exception) {
throw new JSONException(exception);
}
@@ -390,11 +385,11 @@ public class JSONTokener {
final long startIndex = this.index;
final long startCharacter = this.character;
final long startLine = this.line;
this.reader.mark(1000000);
mark(1000000);
do {
c = this.next();
if (c == 0) {
this.reader.reset();
reset();
this.index = startIndex;
this.character = startCharacter;
this.line = startLine;

View File

@@ -6,18 +6,14 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.convert.Converter;
import cn.hutool.core.convert.RegisterConverter;
import cn.hutool.core.convert.impl.ArrayConverter;
import cn.hutool.core.convert.impl.CollectionConverter;
import cn.hutool.core.convert.impl.DateConverter;
import cn.hutool.core.convert.impl.MapConverter;
import cn.hutool.core.convert.impl.TemporalAccessorConverter;
import cn.hutool.core.convert.impl.*;
import cn.hutool.core.map.MapWrapper;
import cn.hutool.core.math.NumberUtil;
import cn.hutool.core.reflect.ConstructorUtil;
import cn.hutool.core.reflect.TypeReference;
import cn.hutool.core.reflect.TypeUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.*;
import cn.hutool.json.serialize.JSONDeserializer;
import cn.hutool.json.serialize.JSONStringer;
@@ -111,36 +107,45 @@ public class JSONConverter implements Converter {
/**
* 实现Object对象转换为{@link JSON},支持的对象:
* <ul>
* <li>String: 转换为相应的对象,</li>
* <li>String: 转换为相应的对象,"和'包围的字符串返回原字符串,""返回{@code null}</li>
* <li>Array、Iterable、Iterator转换为JSONArray</li>
* <li>Bean对象转为JSONObject</li>
* <li>Number返回原对象</li>
* <li>null返回{@code null}</li>
* </ul>
*
* @param obj 被转换的对象
* @return 转换后的对象
* @throws JSONException 转换异常
*/
@SuppressWarnings("resource")
public Object toJSON(final Object obj) throws JSONException {
if(null == obj){
return null;
}
final JSON json;
if (obj instanceof JSON || obj instanceof Number) {
return obj;
} else if (obj instanceof CharSequence) {
final String jsonStr = StrUtil.trim((CharSequence) obj);
switch (jsonStr.charAt(0)){
case '"':
case '\'':
// JSON字符串值
return jsonStr;
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:
if(NumberUtil.isNumber(jsonStr)){
return NumberUtil.toBigDecimal(jsonStr);
// RFC8259JSON字符串值、number, boolean, or null
final Object value = new JSONTokener(jsonStr, config).nextValue();
if(ObjUtil.equals(value, jsonStr)){
// 原值返回意味着非正常数字、Boolean或null
throw new JSONException("Unsupported JSON String: {}", jsonStr);
}
throw new JSONException("Unsupported String to JSON: {}", jsonStr);
return value;
}
} else if (obj instanceof MapWrapper) {
// MapWrapper实现了Iterable会被当作JSONArray此处做修正

View File

@@ -32,7 +32,7 @@ import java.util.function.Predicate;
* @author looly
* @since 6.0.0
*/
public class ArrayMapper {
public class JSONArrayMapper {
/**
* 创建ArrayMapper
*
@@ -40,8 +40,8 @@ public class ArrayMapper {
* @param predicate 值过滤编辑器可以通过实现此接口完成解析前对值的过滤和修改操作{@code null}表示不过滤{@link Predicate#test(Object)}{@code true}保留
* @return ObjectMapper
*/
public static ArrayMapper of(final Object source, final Predicate<Mutable<Object>> predicate) {
return new ArrayMapper(source, predicate);
public static JSONArrayMapper of(final Object source, final Predicate<Mutable<Object>> predicate) {
return new JSONArrayMapper(source, predicate);
}
private final Object source;
@@ -53,7 +53,7 @@ public class ArrayMapper {
* @param source 来源对象
* @param predicate 值过滤编辑器可以通过实现此接口完成解析前对值的过滤和修改操作{@code null}表示不过滤{@link Predicate#test(Object)}{@code true}保留
*/
public ArrayMapper(final Object source, final Predicate<Mutable<Object>> predicate) {
public JSONArrayMapper(final Object source, final Predicate<Mutable<Object>> predicate) {
this.source = source;
this.predicate = predicate;
}
@@ -78,7 +78,9 @@ public class ArrayMapper {
return;
}
if (source instanceof CharSequence) {
if (source instanceof JSONTokener) {
mapFromTokener((JSONTokener) source, jsonArray);
}else if (source instanceof CharSequence) {
// JSON字符串
mapFromStr((CharSequence) source, jsonArray);
} else if (source instanceof Reader) {
@@ -96,8 +98,6 @@ public class ArrayMapper {
jsonArray.add(b);
}
}
} else if (source instanceof JSONTokener) {
mapFromTokener((JSONTokener) source, jsonArray);
} else {
final Iterator<?> iter;
if (ArrayUtil.isArray(source)) {// 数组
@@ -107,7 +107,11 @@ public class ArrayMapper {
} else if (source instanceof Iterable<?>) {// Iterable
iter = ((Iterable<?>) source).iterator();
} else {
throw new JSONException("JSONArray initial value should be a string or collection or array.");
if(false == jsonArray.getConfig().isIgnoreError()){
throw new JSONException("JSONArray initial value should be a string or collection or array.");
}
// 如果用户选择跳过异常则跳过此值转换
return;
}
Object next;

View File

@@ -39,7 +39,7 @@ import java.util.function.Predicate;
* @author looly
* @since 5.8.0
*/
public class ObjectMapper {
public class JSONObjectMapper {
/**
* 创建ObjectMapper
@@ -48,8 +48,8 @@ public class ObjectMapper {
* @param predicate 键值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留
* @return ObjectMapper
*/
public static ObjectMapper of(final Object source, final Predicate<MutableEntry<String, Object>> predicate) {
return new ObjectMapper(source, predicate);
public static JSONObjectMapper of(final Object source, final Predicate<MutableEntry<String, Object>> predicate) {
return new JSONObjectMapper(source, predicate);
}
private final Object source;
@@ -61,7 +61,7 @@ public class ObjectMapper {
* @param source 来源对象
* @param predicate 键值对过滤编辑器可以通过实现此接口完成解析前对键值对的过滤和修改操作{@link Predicate#test(Object)}{@code true}保留
*/
public ObjectMapper(final Object source, final Predicate<MutableEntry<String, Object>> predicate) {
public JSONObjectMapper(final Object source, final Predicate<MutableEntry<String, Object>> predicate) {
this.source = source;
this.predicate = predicate;
}
@@ -90,7 +90,10 @@ public class ObjectMapper {
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
}
if (source instanceof Map) {
if (source instanceof JSONTokener) {
// JSONTokener
mapFromTokener((JSONTokener) source, jsonObject);
}else if (source instanceof Map) {
// Map
for (final Map.Entry<?, ?> e : ((Map<?, ?>) source).entrySet()) {
jsonObject.set(Convert.toStr(e.getKey()), e.getValue(), predicate, false);
@@ -107,18 +110,18 @@ public class ObjectMapper {
mapFromTokener(new JSONTokener((InputStream) source, jsonObject.getConfig()), jsonObject);
} else if (source instanceof byte[]) {
mapFromTokener(new JSONTokener(IoUtil.toStream((byte[]) source), jsonObject.getConfig()), jsonObject);
} else if (source instanceof JSONTokener) {
// JSONTokener
mapFromTokener((JSONTokener) source, jsonObject);
} else if (source instanceof ResourceBundle) {
// JSONTokener
// ResourceBundle
mapFromResourceBundle((ResourceBundle) source, jsonObject);
} else if (BeanUtil.isReadableBean(source.getClass())) {
// 普通Bean
mapFromBean(source, jsonObject);
} else {
// 不支持对象类型转换为JSONObject
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
if(false == jsonObject.getConfig().isIgnoreError()){
// 不支持对象类型转换为JSONObject
throw new JSONException("Unsupported type [{}] to JSONObject!", source.getClass());
}
// 如果用户选择跳过异常则跳过此值转换
}
}

View File

@@ -3,8 +3,6 @@ package cn.hutool.json;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class IssueI6LBZATest {
@Test
public void parseJSONStringTest() {
@@ -31,6 +29,6 @@ public class IssueI6LBZATest {
public void parseJSONNumberTest() {
final String a = "123";
final Object parse = JSONUtil.parse(a);
Assert.assertEquals(BigDecimal.class, parse.getClass());
Assert.assertEquals(Integer.class, parse.getClass());
}
}

View File

@@ -0,0 +1,13 @@
package cn.hutool.json;
import cn.hutool.core.io.resource.ResourceUtil;
import org.junit.Assert;
import org.junit.Test;
public class JSONTokenerTest {
@Test
public void parseTest() {
final JSONObject jsonObject = JSONUtil.parseObj(ResourceUtil.getUtf8Reader("issue1200.json"));
Assert.assertNotNull(jsonObject);
}
}

View File

@@ -17,6 +17,42 @@ import java.util.*;
public class JSONUtilTest {
@Test(expected = JSONException.class)
public void parseInvalid() {
JSONUtil.parse("abc");
}
@Test(expected = JSONException.class)
public void parseInvalid2() {
JSONUtil.parse("'abc");
}
@Test(expected = JSONException.class)
public void parseInvalid3() {
JSONUtil.parse("\"abc");
}
@Test
public void parseValueTest() {
Object parse = JSONUtil.parse(123);
Assert.assertEquals(123, parse);
parse = JSONUtil.parse("\"abc\"");
Assert.assertEquals("abc", parse);
parse = JSONUtil.parse("true");
Assert.assertEquals(true, parse);
parse = JSONUtil.parse("False");
Assert.assertEquals(false, parse);
parse = JSONUtil.parse("null");
Assert.assertNull(parse);
parse = JSONUtil.parse("");
Assert.assertNull(parse);
}
/**
* 出现语法错误时报错,检查解析\x字符时是否会导致死循环异常
*/
@@ -29,20 +65,39 @@ public class JSONUtilTest {
* 数字解析为JSONArray报错
*/
@Test(expected = JSONException.class)
public void parseNumberTest() {
public void parseNumberToJSONArrayTest() {
final JSONArray json = JSONUtil.parseArray(123L);
Assert.assertNotNull(json);
}
/**
* 数字解析为JSONArray报错
*/
@Test
public void parseNumberToJSONArrayTest2() {
final JSONArray json = JSONUtil.parseArray(123L,
JSONConfig.of().setIgnoreError(true));
Assert.assertNotNull(json);
}
/**
* 数字解析为JSONArray报错
*/
@Test(expected = JSONException.class)
public void parseNumberTest2() {
public void parseNumberToJSONObjectTest() {
final JSONObject json = JSONUtil.parseObj(123L);
Assert.assertNotNull(json);
}
/**
* 数字解析为JSONObject忽略错误
*/
@Test
public void parseNumberToJSONObjectTest2() {
final JSONObject json = JSONUtil.parseObj(123L, JSONConfig.of().setIgnoreError(true));
Assert.assertEquals(new JSONObject(), json);
}
@Test
public void toJsonStrTest() {
final UserA a1 = new UserA();