mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix code
This commit is contained in:
parent
75f9a66998
commit
11ba7ba36c
@ -110,7 +110,7 @@ public class JSONParser {
|
||||
}
|
||||
default:
|
||||
tokener.back();
|
||||
key = nextValue(true).toString();
|
||||
key = tokener.nextString();
|
||||
}
|
||||
|
||||
// The key is followed by ':'.
|
||||
@ -120,7 +120,7 @@ public class JSONParser {
|
||||
throw tokener.syntaxError("Expected a ':' after a key");
|
||||
}
|
||||
|
||||
jsonObject.set(key, nextValue(false), predicate);
|
||||
jsonObject.set(key, nextValue(), predicate);
|
||||
|
||||
// Pairs are separated by ','.
|
||||
|
||||
@ -162,7 +162,7 @@ public class JSONParser {
|
||||
jsonArray.addRaw(null, predicate);
|
||||
} else {
|
||||
x.back();
|
||||
jsonArray.addRaw(nextValue(false), predicate);
|
||||
jsonArray.addRaw(nextValue(), predicate);
|
||||
}
|
||||
switch (x.nextClean()) {
|
||||
case CharUtil.COMMA:
|
||||
@ -184,20 +184,19 @@ public class JSONParser {
|
||||
/**
|
||||
* 获得下一个值,值类型可以是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) -> {
|
||||
public Object nextValue() throws JSONException {
|
||||
return nextValue((token, tokener, config) -> {
|
||||
switch (token) {
|
||||
case '{':
|
||||
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 '[':
|
||||
case CharUtil.BRACKET_START:
|
||||
try {
|
||||
return new JSONArray(this, config);
|
||||
} catch (final StackOverflowError e) {
|
||||
@ -211,45 +210,29 @@ public class JSONParser {
|
||||
/**
|
||||
* 获得下一个值,值类型可以是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 {
|
||||
public Object nextValue(final ObjectBuilder objectBuilder) throws JSONException {
|
||||
final JSONTokener tokener = this.tokener;
|
||||
char c = tokener.nextClean();
|
||||
final char c = tokener.nextClean();
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
case CharUtil.DOUBLE_QUOTES:
|
||||
case CharUtil.SINGLE_QUOTE:
|
||||
return tokener.nextString(c);
|
||||
case '{':
|
||||
case '[':
|
||||
if (getOnlyStringValue) {
|
||||
throw tokener.syntaxError("String value must not begin with '{'");
|
||||
}
|
||||
case CharUtil.DELIM_START:
|
||||
case CharUtil.BRACKET_START:
|
||||
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.
|
||||
* 处理无引号包装的字符串,如: true, false, 或 null, 或 number.
|
||||
* 同样兼容非标准的字符串,如key无引号包装。
|
||||
* 此方法会不断读取并积累字符直到遇到token符
|
||||
*/
|
||||
|
||||
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);
|
||||
return InternalJSONUtil.parseValueFromString(tokener.nextUnwrapString(c));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,6 +16,8 @@ import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.ReaderWrapper;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.math.NumberUtil;
|
||||
import org.dromara.hutool.core.text.CharUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -23,12 +25,18 @@ import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* JSON解析器,用于将JSON字符串解析为JSONObject或者JSONArray
|
||||
* JSON解析器<br>
|
||||
* 用于解析JSON字符串,支持流式解析,即逐个字符解析,而不是一次性解析整个字符串。
|
||||
*
|
||||
* @author from JSON.org
|
||||
*/
|
||||
public class JSONTokener extends ReaderWrapper {
|
||||
|
||||
/**
|
||||
* JSON的分界符
|
||||
*/
|
||||
private static final String TOKENS = ",:]}/\\\"[{;=#";
|
||||
|
||||
/**
|
||||
* 定义结束(End of stream)为:0
|
||||
*/
|
||||
@ -207,6 +215,20 @@ public class JSONTokener extends ReaderWrapper {
|
||||
return chars;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个token字符
|
||||
*
|
||||
* @return token字符
|
||||
* @throws JSONException 非Token字符
|
||||
*/
|
||||
public char nextTokenChar() throws JSONException {
|
||||
final char c = this.nextClean();
|
||||
if (isNotTokenChar(c)) {
|
||||
throw this.syntaxError("Invalid token char: " + c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得下一个字符,跳过空白符
|
||||
*
|
||||
@ -223,6 +245,59 @@ public class JSONTokener extends ReaderWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个字符串,包括:
|
||||
* <ul>
|
||||
* <li>使用引号包裹的字符串,自动反转义。</li>
|
||||
* <li>无包装的字符串,不转义</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return 截止到引号前的字符串
|
||||
* @throws JSONException 出现无结束的字符串时抛出此异常
|
||||
*/
|
||||
public String nextString() throws JSONException {
|
||||
final char c = nextClean();
|
||||
switch (c) {
|
||||
case CharUtil.DOUBLE_QUOTES:
|
||||
case CharUtil.SINGLE_QUOTE:
|
||||
return nextString(c);
|
||||
}
|
||||
|
||||
// 兼容不严格的JSON,如key不被双引号包围的情况
|
||||
return nextUnwrapString(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得下一个字符串,此字符串不以引号包围,不会处理转义符,主要解析:
|
||||
* <ul>
|
||||
* <li>非严格的key(无引号包围的key)</li>
|
||||
* <li>boolean值的字符串表示</li>
|
||||
* <li>Number值的字符串表示</li>
|
||||
* <li>null的字符串表示</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param c 首个字符
|
||||
* @return 字符串
|
||||
* @throws JSONException 读取空串时抛出此异常
|
||||
*/
|
||||
public String nextUnwrapString(char c) throws JSONException {
|
||||
// 兼容不严格的JSON,如key不被双引号包围的情况
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
while (isNotTokenChar(c)) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
if (c != EOF) {
|
||||
back();
|
||||
}
|
||||
|
||||
final String valueString = StrUtil.trim(sb);
|
||||
if (valueString.isEmpty()) {
|
||||
throw syntaxError("Missing value, maybe a token");
|
||||
}
|
||||
return valueString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前位置到指定引号前的所有字符,反斜杠的转义符也会被处理。<br>
|
||||
* 标准的JSON是不允许使用单引号包含字符串的,但是此实现允许。
|
||||
@ -237,47 +312,21 @@ public class JSONTokener extends ReaderWrapper {
|
||||
while (true) {
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case EOF:
|
||||
throw this.syntaxError("Unterminated string");
|
||||
case '\n':
|
||||
case '\r':
|
||||
case CharUtil.LF:
|
||||
case CharUtil.CR:
|
||||
//throw this.syntaxError("Unterminated string");
|
||||
// https://gitee.com/dromara/hutool/issues/I76CSU
|
||||
// 兼容非转义符
|
||||
sb.append(c);
|
||||
break;
|
||||
case '\\':// 转义符
|
||||
case CharUtil.BACKSLASH:// 转义符
|
||||
c = this.next();
|
||||
switch (c) {
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 'u':// Unicode符
|
||||
sb.append(nextUnicode());
|
||||
break;
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
case '/':
|
||||
sb.append(c);
|
||||
break;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
sb.append(getUnescapeChar(c));
|
||||
break;
|
||||
default:
|
||||
// 字符串结束
|
||||
if (c == quote) {
|
||||
return sb.toString();
|
||||
}
|
||||
@ -286,80 +335,6 @@ public class JSONTokener extends ReaderWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得从当前位置直到分隔符(不包括分隔符)或行尾的的所有字符。
|
||||
*
|
||||
* @param delimiter 分隔符
|
||||
* @return 字符串
|
||||
* @throws JSONException JSON异常,包装IO异常
|
||||
*/
|
||||
public String nextTo(final char delimiter) throws JSONException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
final char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text up but not including one of the specified delimiter characters or the end of line, whichever comes first.
|
||||
*
|
||||
* @param delimiters A set of delimiter characters.
|
||||
* @return A string, trimmed.
|
||||
* @throws JSONException JSON异常,包装IO异常
|
||||
*/
|
||||
public String nextTo(final String delimiters) throws JSONException {
|
||||
char c;
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (; ; ) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip characters until the next character is the requested character. If the requested character is not found, no characters are skipped. 在遇到指定字符前,跳过其它字符。如果字符未找到,则不跳过任何字符。
|
||||
*
|
||||
* @param to 需要定位的字符
|
||||
* @return 定位的字符,如果字符未找到返回0
|
||||
* @throws JSONException IO异常
|
||||
*/
|
||||
public char skipTo(final char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
final long startIndex = this.index;
|
||||
final long startCharacter = this.character;
|
||||
final long startLine = this.line;
|
||||
mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (final IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error. <br>
|
||||
* 构建 JSONException 用于表示语法错误
|
||||
@ -380,4 +355,44 @@ public class JSONTokener extends ReaderWrapper {
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " + this.line + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取反转义的字符
|
||||
*
|
||||
* @param c 转义的字符,即`\`后的字符
|
||||
* @return 反转义字符
|
||||
*/
|
||||
private char getUnescapeChar(final char c) {
|
||||
switch (c) {
|
||||
case 'b':
|
||||
return '\b';
|
||||
case 't':
|
||||
return '\t';
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 'f':
|
||||
return '\f';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 'u':// Unicode符
|
||||
return nextUnicode();
|
||||
case CharUtil.DOUBLE_QUOTES:
|
||||
case CharUtil.SINGLE_QUOTE:
|
||||
case CharUtil.BACKSLASH:
|
||||
case CharUtil.SLASH:
|
||||
return c;
|
||||
default:
|
||||
throw this.syntaxError("Illegal escape.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为可见的非Token字符,这些字符存在于JSON的非字符串value中。
|
||||
*
|
||||
* @param c char
|
||||
* @return 是否为可见的非Token字符
|
||||
*/
|
||||
private static boolean isNotTokenChar(final char c) {
|
||||
return c >= ' ' && TOKENS.indexOf(c) < 0;
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ public class JSONConverter implements Converter, Serializable {
|
||||
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);
|
||||
final Object value = jsonParser.nextValue();
|
||||
if(jsonParser.getTokener().nextClean() != JSONTokener.EOF){
|
||||
// 对于用户提供的未转义字符串导致解析未结束,报错
|
||||
throw new JSONException("JSON format error: {}", jsonStr);
|
||||
|
@ -12,14 +12,48 @@
|
||||
|
||||
package org.dromara.hutool.json;
|
||||
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.resource.ResourceUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class JSONTokenerTest {
|
||||
@Test
|
||||
public void parseTest() {
|
||||
void parseTest() {
|
||||
final JSONObject jsonObject = JSONUtil.parseObj(ResourceUtil.getUtf8Reader("issue1200.json"));
|
||||
Assertions.assertNotNull(jsonObject);
|
||||
assertNotNull(jsonObject);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nextTest() {
|
||||
final JSONTokener jsonTokener = new JSONTokener("{\"ab\": \"abc\"}");
|
||||
final char c = jsonTokener.nextTokenChar();
|
||||
assertEquals('{', c);
|
||||
assertEquals("ab", jsonTokener.nextString());
|
||||
final char c2 = jsonTokener.nextTokenChar();
|
||||
assertEquals(':', c2);
|
||||
assertEquals("abc", jsonTokener.nextString());
|
||||
|
||||
|
||||
IoUtil.closeQuietly(jsonTokener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容非包装符包装的value和key
|
||||
*/
|
||||
@Test
|
||||
void nextWithoutWrapperTest() {
|
||||
final JSONTokener jsonTokener = new JSONTokener("{ab: abc}");
|
||||
final char c = jsonTokener.nextTokenChar();
|
||||
assertEquals('{', c);
|
||||
assertEquals("ab", jsonTokener.nextString());
|
||||
final char c2 = jsonTokener.nextTokenChar();
|
||||
assertEquals(':', c2);
|
||||
assertEquals("abc", jsonTokener.nextString());
|
||||
|
||||
|
||||
IoUtil.closeQuietly(jsonTokener);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user