mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add new JSONParser
This commit is contained in:
parent
fe676fd933
commit
7debdcef60
@ -29,7 +29,13 @@ import java.lang.reflect.Type;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON接口
|
* JSON树模型接口,表示树中的一个节点。实现包括:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link JSONObject}表示键值对形式的节点</li>
|
||||||
|
* <li>{@link JSONArray}表示列表形式的节点</li>
|
||||||
|
* <li>{@link JSONPrimitive}表示数字、Boolean、字符串形式的节点</li>
|
||||||
|
* <li>{@code null}表示空节点</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
|
@ -30,7 +30,7 @@ import org.dromara.hutool.core.reflect.TypeUtil;
|
|||||||
import org.dromara.hutool.core.reflect.kotlin.KClassUtil;
|
import org.dromara.hutool.core.reflect.kotlin.KClassUtil;
|
||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
import org.dromara.hutool.json.*;
|
import org.dromara.hutool.json.*;
|
||||||
import org.dromara.hutool.json.reader.JSONParser;
|
import org.dromara.hutool.json.reader.OldJSONParser;
|
||||||
import org.dromara.hutool.json.reader.JSONTokener;
|
import org.dromara.hutool.json.reader.JSONTokener;
|
||||||
import org.dromara.hutool.json.serializer.*;
|
import org.dromara.hutool.json.serializer.*;
|
||||||
|
|
||||||
@ -202,7 +202,7 @@ public class JSONConverter implements Converter, Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RFC8259,JSON字符串值、number, boolean, or null
|
// RFC8259,JSON字符串值、number, boolean, or null
|
||||||
final JSONParser jsonParser = JSONParser.of(new JSONTokener(jsonStr), config);
|
final OldJSONParser jsonParser = OldJSONParser.of(new JSONTokener(jsonStr), config);
|
||||||
final Object value = jsonParser.nextValue();
|
final Object value = jsonParser.nextValue();
|
||||||
if (jsonParser.getTokener().nextClean() != JSONTokener.EOF) {
|
if (jsonParser.getTokener().nextClean() != JSONTokener.EOF) {
|
||||||
// 对于用户提供的未转义字符串导致解析未结束,报错
|
// 对于用户提供的未转义字符串导致解析未结束,报错
|
||||||
|
@ -24,7 +24,7 @@ import org.dromara.hutool.core.text.StrUtil;
|
|||||||
import org.dromara.hutool.json.JSONArray;
|
import org.dromara.hutool.json.JSONArray;
|
||||||
import org.dromara.hutool.json.JSONConfig;
|
import org.dromara.hutool.json.JSONConfig;
|
||||||
import org.dromara.hutool.json.JSONException;
|
import org.dromara.hutool.json.JSONException;
|
||||||
import org.dromara.hutool.json.reader.JSONParser;
|
import org.dromara.hutool.json.reader.OldJSONParser;
|
||||||
import org.dromara.hutool.json.reader.JSONTokener;
|
import org.dromara.hutool.json.reader.JSONTokener;
|
||||||
import org.dromara.hutool.json.serializer.JSONSerializer;
|
import org.dromara.hutool.json.serializer.JSONSerializer;
|
||||||
import org.dromara.hutool.json.serializer.SerializerManager;
|
import org.dromara.hutool.json.serializer.SerializerManager;
|
||||||
@ -98,8 +98,8 @@ public class JSONArrayMapper {
|
|||||||
|
|
||||||
if (source instanceof JSONTokener) {
|
if (source instanceof JSONTokener) {
|
||||||
mapFromTokener((JSONTokener) source, JSONConfig.of(), jsonArray);
|
mapFromTokener((JSONTokener) source, JSONConfig.of(), jsonArray);
|
||||||
}if (source instanceof JSONParser) {
|
}if (source instanceof OldJSONParser) {
|
||||||
((JSONParser)source).parseTo(jsonArray, this.predicate);
|
((OldJSONParser)source).parseTo(jsonArray, this.predicate);
|
||||||
} else if (source instanceof CharSequence) {
|
} else if (source instanceof CharSequence) {
|
||||||
// JSON字符串
|
// JSON字符串
|
||||||
mapFromStr((CharSequence) source, jsonArray);
|
mapFromStr((CharSequence) source, jsonArray);
|
||||||
@ -164,6 +164,6 @@ public class JSONArrayMapper {
|
|||||||
* @param jsonArray {@link JSONArray}
|
* @param jsonArray {@link JSONArray}
|
||||||
*/
|
*/
|
||||||
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONArray jsonArray) {
|
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONArray jsonArray) {
|
||||||
JSONParser.of(x, config).parseTo(jsonArray, this.predicate);
|
OldJSONParser.of(x, config).parseTo(jsonArray, this.predicate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import org.dromara.hutool.core.lang.mutable.MutableEntry;
|
|||||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||||
import org.dromara.hutool.core.text.StrUtil;
|
import org.dromara.hutool.core.text.StrUtil;
|
||||||
import org.dromara.hutool.json.*;
|
import org.dromara.hutool.json.*;
|
||||||
import org.dromara.hutool.json.reader.JSONParser;
|
import org.dromara.hutool.json.reader.OldJSONParser;
|
||||||
import org.dromara.hutool.json.reader.JSONTokener;
|
import org.dromara.hutool.json.reader.JSONTokener;
|
||||||
import org.dromara.hutool.json.serializer.JSONSerializer;
|
import org.dromara.hutool.json.serializer.JSONSerializer;
|
||||||
import org.dromara.hutool.json.serializer.SerializerManager;
|
import org.dromara.hutool.json.serializer.SerializerManager;
|
||||||
@ -111,9 +111,9 @@ public class JSONObjectMapper {
|
|||||||
if (source instanceof JSONTokener) {
|
if (source instanceof JSONTokener) {
|
||||||
// JSONTokener
|
// JSONTokener
|
||||||
mapFromTokener((JSONTokener) source, jsonObject.config(), jsonObject);
|
mapFromTokener((JSONTokener) source, jsonObject.config(), jsonObject);
|
||||||
}if (source instanceof JSONParser) {
|
}if (source instanceof OldJSONParser) {
|
||||||
// JSONParser
|
// JSONParser
|
||||||
((JSONParser) source).parseTo(jsonObject, this.predicate);
|
((OldJSONParser) source).parseTo(jsonObject, this.predicate);
|
||||||
} else if (source instanceof Map) {
|
} else if (source instanceof Map) {
|
||||||
// Map
|
// Map
|
||||||
for (final Map.Entry<?, ?> e : ((Map<?, ?>) source).entrySet()) {
|
for (final Map.Entry<?, ?> e : ((Map<?, ?>) source).entrySet()) {
|
||||||
@ -191,7 +191,7 @@ public class JSONObjectMapper {
|
|||||||
* @param jsonObject {@link JSONObject}
|
* @param jsonObject {@link JSONObject}
|
||||||
*/
|
*/
|
||||||
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONObject jsonObject) {
|
private void mapFromTokener(final JSONTokener x, final JSONConfig config, final JSONObject jsonObject) {
|
||||||
JSONParser.of(x, config).parseTo(jsonObject, this.predicate);
|
OldJSONParser.of(x, config).parseTo(jsonObject, this.predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.json.reader;
|
package org.dromara.hutool.json.reader;
|
||||||
|
|
||||||
import org.dromara.hutool.core.lang.mutable.Mutable;
|
|
||||||
import org.dromara.hutool.core.lang.mutable.MutableEntry;
|
import org.dromara.hutool.core.lang.mutable.MutableEntry;
|
||||||
import org.dromara.hutool.core.text.CharUtil;
|
import org.dromara.hutool.core.text.CharUtil;
|
||||||
import org.dromara.hutool.json.*;
|
import org.dromara.hutool.json.*;
|
||||||
@ -31,7 +30,7 @@ import java.util.function.Predicate;
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
* @since 5.8.0
|
* @since 6.0.0
|
||||||
*/
|
*/
|
||||||
public class JSONParser {
|
public class JSONParser {
|
||||||
|
|
||||||
@ -51,6 +50,7 @@ public class JSONParser {
|
|||||||
*/
|
*/
|
||||||
private final JSONConfig config;
|
private final JSONConfig config;
|
||||||
private final JSONTokener tokener;
|
private final JSONTokener tokener;
|
||||||
|
private Predicate<MutableEntry<Object, Object>> predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
@ -73,72 +73,94 @@ public class JSONParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否结束
|
* 获取下一个值,可以是:
|
||||||
|
* <pre>
|
||||||
|
* JSONObject
|
||||||
|
* JSONArray
|
||||||
|
* JSONPrimitive
|
||||||
|
* null
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @return 是否结束
|
* @return JSON值
|
||||||
*/
|
*/
|
||||||
public boolean end() {
|
public JSON nextValue() {
|
||||||
return this.tokener.end();
|
return nextValue(tokener.nextClean());
|
||||||
}
|
}
|
||||||
|
|
||||||
// region parseTo
|
/**
|
||||||
|
* 获取下一个值,可以是:
|
||||||
|
* <pre>
|
||||||
|
* JSONObject
|
||||||
|
* JSONArray
|
||||||
|
* JSONPrimitive
|
||||||
|
* null
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return JSON值
|
||||||
|
*/
|
||||||
|
private JSON nextValue(final char c) {
|
||||||
|
switch (c) {
|
||||||
|
case CharUtil.DELIM_START:
|
||||||
|
final JSONObject jsonObject = new JSONObject(tokener, config);
|
||||||
|
nextJSONObject(jsonObject);
|
||||||
|
return jsonObject;
|
||||||
|
case CharUtil.BRACKET_START:
|
||||||
|
final JSONArray jsonArray = new JSONArray(tokener, config);
|
||||||
|
nextJSONArray(jsonArray);
|
||||||
|
return jsonArray;
|
||||||
|
default:
|
||||||
|
return nextJSONPrimitive(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析{@link JSONTokener}中的字符到目标的{@link JSONObject}中
|
* 解析为JSONObject
|
||||||
*
|
*
|
||||||
* @param jsonObject {@link JSONObject}
|
* @param jsonObject JSON对象
|
||||||
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留
|
|
||||||
*/
|
*/
|
||||||
public void parseTo(final JSONObject jsonObject, final Predicate<MutableEntry<String, Object>> predicate) {
|
private void nextJSONObject(final JSONObject jsonObject) {
|
||||||
final JSONTokener tokener = this.tokener;
|
final JSONTokener tokener = this.tokener;
|
||||||
|
|
||||||
if (tokener.nextClean() != '{') {
|
|
||||||
throw tokener.syntaxError("A JSONObject text must begin with '{'");
|
|
||||||
}
|
|
||||||
|
|
||||||
char prev;
|
|
||||||
char c;
|
char c;
|
||||||
String key;
|
String key;
|
||||||
while (true) {
|
for (; ; ) {
|
||||||
prev = tokener.getPrevious();
|
|
||||||
c = tokener.nextClean();
|
c = tokener.nextClean();
|
||||||
switch (c) {
|
if (c == CharUtil.DELIM_END) {// 对象结束
|
||||||
case 0:
|
return;
|
||||||
throw tokener.syntaxError("A JSONObject text must end with '}'");
|
} else {
|
||||||
case '}':
|
key = tokener.nextKey(c);
|
||||||
return;
|
|
||||||
case '{':
|
|
||||||
case '[':
|
|
||||||
if (prev == '{') {
|
|
||||||
throw tokener.syntaxError("A JSONObject can not directly nest another JSONObject or JSONArray.");
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
tokener.back();
|
|
||||||
key = tokener.nextString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The key is followed by ':'.
|
// The key is followed by ':'.
|
||||||
|
tokener.nextColon();
|
||||||
|
|
||||||
c = tokener.nextClean();
|
// 过滤并设置键值对
|
||||||
if (c != ':') {
|
Object value = nextValue();
|
||||||
throw tokener.syntaxError("Expected a ':' after a key");
|
// 添加前置过滤,通过MutablePair实现过滤、修改键值对等
|
||||||
|
if (null != predicate) {
|
||||||
|
final MutableEntry<Object, Object> pair = new MutableEntry<>(key, value);
|
||||||
|
if (predicate.test(pair)) {
|
||||||
|
// 使用修改后的键值对
|
||||||
|
key = (String) pair.getKey();
|
||||||
|
value = pair.getValue();
|
||||||
|
jsonObject.set(key, value);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
jsonObject.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonObject.set(key, nextValue(), predicate);
|
// Pairs are separated by ',' or ';'
|
||||||
|
|
||||||
// Pairs are separated by ','.
|
|
||||||
|
|
||||||
switch (tokener.nextClean()) {
|
switch (tokener.nextClean()) {
|
||||||
case ';':
|
case ';':
|
||||||
case CharUtil.COMMA:
|
case CharUtil.COMMA:
|
||||||
if (tokener.nextClean() == '}') {
|
if (tokener.nextClean() == CharUtil.DELIM_END) {
|
||||||
// issue#2380
|
// issue#2380
|
||||||
// 尾后逗号(Trailing Commas),JSON中虽然不支持,但是ECMAScript 2017支持,此处做兼容。
|
// 尾后逗号(Trailing Commas),JSON中虽然不支持,但是ECMAScript 2017支持,此处做兼容。
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tokener.back();
|
tokener.back();
|
||||||
break;
|
break;
|
||||||
case '}':
|
case CharUtil.DELIM_END:
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
throw tokener.syntaxError("Expected a ',' or '}'");
|
throw tokener.syntaxError("Expected a ',' or '}'");
|
||||||
@ -147,111 +169,58 @@ public class JSONParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析JSON字符串到{@link JSONArray}中
|
* 解析为JSONArray
|
||||||
*
|
*
|
||||||
* @param jsonArray {@link JSONArray}
|
* @param jsonArray JSON数组
|
||||||
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null} 表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留
|
|
||||||
*/
|
*/
|
||||||
public void parseTo(final JSONArray jsonArray, final Predicate<Mutable<Object>> predicate) {
|
private void nextJSONArray(final JSONArray jsonArray) {
|
||||||
final JSONTokener x = this.tokener;
|
|
||||||
|
|
||||||
if (x.nextClean() != '[') {
|
|
||||||
throw x.syntaxError("A JSONArray text must start with '['");
|
|
||||||
}
|
|
||||||
if (x.nextClean() != ']') {
|
|
||||||
x.back();
|
|
||||||
for (; ; ) {
|
|
||||||
if (x.nextClean() == CharUtil.COMMA) {
|
|
||||||
x.back();
|
|
||||||
jsonArray.add(null, predicate);
|
|
||||||
} else {
|
|
||||||
x.back();
|
|
||||||
jsonArray.add(nextValue(), predicate);
|
|
||||||
}
|
|
||||||
switch (x.nextClean()) {
|
|
||||||
case CharUtil.COMMA:
|
|
||||||
if (x.nextClean() == ']') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
x.back();
|
|
||||||
break;
|
|
||||||
case ']':
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
throw x.syntaxError("Expected a ',' or ']'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
|
|
||||||
*
|
|
||||||
* @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
|
|
||||||
* @throws JSONException 语法错误
|
|
||||||
*/
|
|
||||||
public Object nextValue() throws JSONException {
|
|
||||||
return nextValue((token, tokener, config) -> {
|
|
||||||
switch (token) {
|
|
||||||
case CharUtil.DELIM_START:
|
|
||||||
try {
|
|
||||||
return new JSONObject(this, config);
|
|
||||||
} catch (final StackOverflowError e) {
|
|
||||||
throw new JSONException("JSONObject depth too large to process.", e);
|
|
||||||
}
|
|
||||||
case CharUtil.BRACKET_START:
|
|
||||||
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 objectBuilder JSON对象构建器
|
|
||||||
* @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
|
|
||||||
* @throws JSONException 语法错误
|
|
||||||
*/
|
|
||||||
public Object nextValue(final ObjectBuilder objectBuilder) throws JSONException {
|
|
||||||
final JSONTokener tokener = this.tokener;
|
final JSONTokener tokener = this.tokener;
|
||||||
final char c = tokener.nextClean();
|
char c;
|
||||||
|
for (; ; ) {
|
||||||
|
c = tokener.nextClean();
|
||||||
|
switch (c) {
|
||||||
|
case CharUtil.BRACKET_END:
|
||||||
|
return;
|
||||||
|
case CharUtil.COMMA:
|
||||||
|
jsonArray.add(nextValue());
|
||||||
|
default:
|
||||||
|
Object value = CharUtil.COMMA == c ? nextValue() : nextValue(c);
|
||||||
|
if (null != predicate) {
|
||||||
|
// 使用过滤器
|
||||||
|
final MutableEntry<Object, Object> pair = MutableEntry.of(jsonArray.size(), value);
|
||||||
|
if (predicate.test(pair)) {
|
||||||
|
// 使用修改后的键值对
|
||||||
|
value = pair.getValue();
|
||||||
|
jsonArray.add(value);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
jsonArray.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析为JSONPrimitive或{@code null},解析值包括:
|
||||||
|
* <pre>
|
||||||
|
* boolean
|
||||||
|
* number
|
||||||
|
* string
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param c 值类型
|
||||||
|
* @return JSONPrimitive或{@code null}
|
||||||
|
*/
|
||||||
|
private JSONPrimitive nextJSONPrimitive(final char c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case CharUtil.DOUBLE_QUOTES:
|
case CharUtil.DOUBLE_QUOTES:
|
||||||
case CharUtil.SINGLE_QUOTE:
|
case CharUtil.SINGLE_QUOTE:
|
||||||
return tokener.nextString(c);
|
// 引号包围,表示字符串值
|
||||||
case CharUtil.DELIM_START:
|
return new JSONPrimitive(tokener.nextWrapString(c));
|
||||||
case CharUtil.BRACKET_START:
|
default:
|
||||||
tokener.back();
|
final Object value = InternalJSONUtil.parseValueFromString(tokener.nextUnwrapString(c));
|
||||||
return objectBuilder.build(c, tokener, this.config);
|
// 非引号包围,可能为boolean、数字、null等
|
||||||
|
return null == value ? null : new JSONPrimitive(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 处理无引号包装的字符串,如: true, false, 或 null, 或 number.
|
|
||||||
* 同样兼容非标准的字符串,如key无引号包装。
|
|
||||||
* 此方法会不断读取并积累字符直到遇到token符
|
|
||||||
*/
|
|
||||||
return InternalJSONUtil.parseValueFromString(tokener.nextUnwrapString(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象构建抽象,通过实现此接口,从{@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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,12 +244,55 @@ public class JSONTokener extends ReaderWrapper {
|
|||||||
char c;
|
char c;
|
||||||
while (true) {
|
while (true) {
|
||||||
c = this.next();
|
c = this.next();
|
||||||
if (c == 0 || c > ' ') {
|
if (c == EOF || c > ' ') {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取下一个JSON中的key,支持不带引号的key
|
||||||
|
*
|
||||||
|
* @param c 当前字符
|
||||||
|
* @return 键字符串
|
||||||
|
* @throws JSONException 非引号包裹的字符串
|
||||||
|
*/
|
||||||
|
public String nextKey(final char c) throws JSONException {
|
||||||
|
final char prev = this.previous;
|
||||||
|
switch (c) {
|
||||||
|
case JSONTokener.EOF:
|
||||||
|
// 未关闭对象
|
||||||
|
throw syntaxError("A JSONObject text must end with '}'");
|
||||||
|
case CharUtil.DELIM_START:
|
||||||
|
case CharUtil.BRACKET_END:
|
||||||
|
if (prev == CharUtil.DELIM_START) {
|
||||||
|
// 不允许嵌套对象,如{{}}或{[]}
|
||||||
|
throw syntaxError("A JSONObject can not directly nest another JSONObject or JSONArray.");
|
||||||
|
}
|
||||||
|
case CharUtil.DOUBLE_QUOTES:
|
||||||
|
case CharUtil.SINGLE_QUOTE:
|
||||||
|
// 带有包装的key
|
||||||
|
return nextWrapString(c);
|
||||||
|
default:
|
||||||
|
// 兼容不严格的JSON,如key不被引号包围的情况
|
||||||
|
return nextUnwrapString(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个冒号,非冒号则抛出异常
|
||||||
|
*
|
||||||
|
* @throws JSONException 非冒号字符
|
||||||
|
* @return 冒号字符
|
||||||
|
*/
|
||||||
|
public char nextColon() throws JSONException {
|
||||||
|
final char c = nextClean();
|
||||||
|
if (c != CharUtil.COLON) {
|
||||||
|
throw syntaxError("Expected a ':' after a key");
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取一个字符串,包括:
|
* 读取一个字符串,包括:
|
||||||
* <ul>
|
* <ul>
|
||||||
@ -265,7 +308,7 @@ public class JSONTokener extends ReaderWrapper {
|
|||||||
switch (c) {
|
switch (c) {
|
||||||
case CharUtil.DOUBLE_QUOTES:
|
case CharUtil.DOUBLE_QUOTES:
|
||||||
case CharUtil.SINGLE_QUOTE:
|
case CharUtil.SINGLE_QUOTE:
|
||||||
return nextString(c);
|
return nextWrapString(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兼容不严格的JSON,如key不被双引号包围的情况
|
// 兼容不严格的JSON,如key不被双引号包围的情况
|
||||||
@ -311,7 +354,7 @@ public class JSONTokener extends ReaderWrapper {
|
|||||||
* @return 截止到引号前的字符串
|
* @return 截止到引号前的字符串
|
||||||
* @throws JSONException 出现无结束的字符串时抛出此异常
|
* @throws JSONException 出现无结束的字符串时抛出此异常
|
||||||
*/
|
*/
|
||||||
public String nextString(final char quote) throws JSONException {
|
public String nextWrapString(final char quote) throws JSONException {
|
||||||
char c;
|
char c;
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||||
|
*
|
||||||
|
* 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 org.dromara.hutool.json.reader;
|
||||||
|
|
||||||
|
import org.dromara.hutool.core.lang.mutable.Mutable;
|
||||||
|
import org.dromara.hutool.core.lang.mutable.MutableEntry;
|
||||||
|
import org.dromara.hutool.core.text.CharUtil;
|
||||||
|
import org.dromara.hutool.json.*;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON字符串解析器,实现:
|
||||||
|
* <ul>
|
||||||
|
* <li>JSON字符串 --> {@link JSONTokener} --> {@link JSONObject}</li>
|
||||||
|
* <li>JSON字符串 --> {@link JSONTokener} --> {@link JSONArray}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class OldJSONParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建JSONParser
|
||||||
|
*
|
||||||
|
* @param tokener {@link JSONTokener}
|
||||||
|
* @param config JSON配置
|
||||||
|
* @return JSONParser
|
||||||
|
*/
|
||||||
|
public static OldJSONParser of(final JSONTokener tokener, final JSONConfig config) {
|
||||||
|
return new OldJSONParser(tokener, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON配置
|
||||||
|
*/
|
||||||
|
private final JSONConfig config;
|
||||||
|
private final JSONTokener tokener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param tokener {@link JSONTokener}
|
||||||
|
* @param config JSON配置
|
||||||
|
*/
|
||||||
|
public OldJSONParser(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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析{@link JSONTokener}中的字符到目标的{@link JSONObject}中
|
||||||
|
*
|
||||||
|
* @param jsonObject {@link JSONObject}
|
||||||
|
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对键值对的过滤和修改操作,{@code null}表示不过滤,{@link Predicate#test(Object)}为{@code true}保留
|
||||||
|
*/
|
||||||
|
public void parseTo(final JSONObject jsonObject, final Predicate<MutableEntry<String, Object>> predicate) {
|
||||||
|
final JSONTokener tokener = this.tokener;
|
||||||
|
|
||||||
|
if (tokener.nextClean() != '{') {
|
||||||
|
throw tokener.syntaxError("A JSONObject text must begin with '{'");
|
||||||
|
}
|
||||||
|
|
||||||
|
char prev;
|
||||||
|
char c;
|
||||||
|
String key;
|
||||||
|
while (true) {
|
||||||
|
prev = tokener.getPrevious();
|
||||||
|
c = tokener.nextClean();
|
||||||
|
switch (c) {
|
||||||
|
case 0:
|
||||||
|
throw tokener.syntaxError("A JSONObject text must end with '}'");
|
||||||
|
case '}':
|
||||||
|
return;
|
||||||
|
case '{':
|
||||||
|
case '[':
|
||||||
|
if (prev == '{') {
|
||||||
|
throw tokener.syntaxError("A JSONObject can not directly nest another JSONObject or JSONArray.");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
tokener.back();
|
||||||
|
key = tokener.nextString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The key is followed by ':'.
|
||||||
|
|
||||||
|
c = tokener.nextClean();
|
||||||
|
if (c != ':') {
|
||||||
|
throw tokener.syntaxError("Expected a ':' after a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonObject.set(key, nextValue(), predicate);
|
||||||
|
|
||||||
|
// Pairs are separated by ','.
|
||||||
|
|
||||||
|
switch (tokener.nextClean()) {
|
||||||
|
case ';':
|
||||||
|
case CharUtil.COMMA:
|
||||||
|
if (tokener.nextClean() == '}') {
|
||||||
|
// issue#2380
|
||||||
|
// 尾后逗号(Trailing Commas),JSON中虽然不支持,但是ECMAScript 2017支持,此处做兼容。
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tokener.back();
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw tokener.syntaxError("Expected a ',' or '}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析JSON字符串到{@link JSONArray}中
|
||||||
|
*
|
||||||
|
* @param jsonArray {@link JSONArray}
|
||||||
|
* @param predicate 键值对过滤编辑器,可以通过实现此接口,完成解析前对值的过滤和修改操作,{@code null} 表示不过滤,,{@link Predicate#test(Object)}为{@code true}保留
|
||||||
|
*/
|
||||||
|
public void parseTo(final JSONArray jsonArray, final Predicate<Mutable<Object>> predicate) {
|
||||||
|
final JSONTokener x = this.tokener;
|
||||||
|
|
||||||
|
if (x.nextClean() != '[') {
|
||||||
|
throw x.syntaxError("A JSONArray text must start with '['");
|
||||||
|
}
|
||||||
|
if (x.nextClean() != ']') {
|
||||||
|
x.back();
|
||||||
|
for (; ; ) {
|
||||||
|
if (x.nextClean() == CharUtil.COMMA) {
|
||||||
|
x.back();
|
||||||
|
jsonArray.add(null, predicate);
|
||||||
|
} else {
|
||||||
|
x.back();
|
||||||
|
jsonArray.add(nextValue(), predicate);
|
||||||
|
}
|
||||||
|
switch (x.nextClean()) {
|
||||||
|
case CharUtil.COMMA:
|
||||||
|
if (x.nextClean() == ']') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x.back();
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
throw x.syntaxError("Expected a ',' or ']'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得下一个值,值类型可以是Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
|
||||||
|
*
|
||||||
|
* @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
|
||||||
|
* @throws JSONException 语法错误
|
||||||
|
*/
|
||||||
|
public Object nextValue() throws JSONException {
|
||||||
|
return nextValue((token, tokener, config) -> {
|
||||||
|
switch (token) {
|
||||||
|
case CharUtil.DELIM_START:
|
||||||
|
try {
|
||||||
|
return new JSONObject(this, config);
|
||||||
|
} catch (final StackOverflowError e) {
|
||||||
|
throw new JSONException("JSONObject depth too large to process.", e);
|
||||||
|
}
|
||||||
|
case CharUtil.BRACKET_START:
|
||||||
|
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 objectBuilder JSON对象构建器
|
||||||
|
* @return Boolean, Double, Integer, JSONArray, JSONObject, Long, or String
|
||||||
|
* @throws JSONException 语法错误
|
||||||
|
*/
|
||||||
|
public Object nextValue(final ObjectBuilder objectBuilder) throws JSONException {
|
||||||
|
final JSONTokener tokener = this.tokener;
|
||||||
|
final char c = tokener.nextClean();
|
||||||
|
switch (c) {
|
||||||
|
case CharUtil.DOUBLE_QUOTES:
|
||||||
|
case CharUtil.SINGLE_QUOTE:
|
||||||
|
return tokener.nextWrapString(c);
|
||||||
|
case CharUtil.DELIM_START:
|
||||||
|
case CharUtil.BRACKET_START:
|
||||||
|
tokener.back();
|
||||||
|
return objectBuilder.build(c, tokener, this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 处理无引号包装的字符串,如: true, false, 或 null, 或 number.
|
||||||
|
* 同样兼容非标准的字符串,如key无引号包装。
|
||||||
|
* 此方法会不断读取并积累字符直到遇到token符
|
||||||
|
*/
|
||||||
|
return InternalJSONUtil.parseValueFromString(tokener.nextUnwrapString(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象构建抽象,通过实现此接口,从{@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);
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON读取和解析
|
* JSON读取和解析,主要解析字符串、流等JSON字符串为{@link org.dromara.hutool.json.JSON}。
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user