feat: 统一 parser 的异常
This commit is contained in:
parent
b730b53f7b
commit
f1086a1ef9
@ -0,0 +1,11 @@
|
||||
package org.jcnc.snow.compiler.parser.context;
|
||||
|
||||
/**
|
||||
* 当语法结构缺失必须出现的 Token 时抛出。
|
||||
*/
|
||||
public final class MissingToken extends ParseException {
|
||||
|
||||
public MissingToken(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,19 @@
|
||||
package org.jcnc.snow.compiler.parser.context;
|
||||
|
||||
/**
|
||||
* {@code ParseException} 表示语法分析阶段发生的错误。
|
||||
* <p>
|
||||
* 当语法分析器遇到非法的语法结构或无法继续处理的标记序列时,
|
||||
* 应抛出该异常以中断当前解析流程,并向调用方报告错误信息。
|
||||
* </p>
|
||||
* <p>
|
||||
* 该异常通常由 {@code ParserContext} 或各类语法规则处理器主动抛出,
|
||||
* 用于提示编译器前端或 IDE 系统进行错误提示与恢复。
|
||||
* </p>
|
||||
* {@code ParseException}——语法分析阶段所有错误的基类。
|
||||
*
|
||||
* <p>声明为 <em>sealed</em>,仅允许 {@link UnexpectedToken}、
|
||||
* {@link MissingToken}、{@link UnsupportedFeature} 三个受信子类继承,
|
||||
* 以便调用方根据异常类型进行精确处理。</p>
|
||||
*/
|
||||
public class ParseException extends RuntimeException {
|
||||
public sealed class ParseException extends RuntimeException
|
||||
permits UnexpectedToken, MissingToken, UnsupportedFeature {
|
||||
|
||||
/**
|
||||
* 构造一个带有错误描述信息的解析异常实例。
|
||||
* 构造解析异常并附带错误消息。
|
||||
*
|
||||
* @param message 错误描述文本,用于指明具体的语法错误原因
|
||||
* @param message 错误描述
|
||||
*/
|
||||
public ParseException(String message) {
|
||||
super(message);
|
||||
|
||||
@ -31,7 +31,7 @@ public class TokenStream {
|
||||
*/
|
||||
public TokenStream(List<Token> tokens) {
|
||||
if (tokens == null) {
|
||||
throw new NullPointerException("Token list cannot be null.");
|
||||
throw new NullPointerException("Token 列表不能为空");
|
||||
}
|
||||
this.tokens = tokens;
|
||||
}
|
||||
@ -103,8 +103,8 @@ public class TokenStream {
|
||||
Token t = peek();
|
||||
if (!t.getLexeme().equals(lexeme)) {
|
||||
throw new ParseException(
|
||||
"Expected lexeme '" + lexeme + "' but got '" + t.getLexeme() +
|
||||
"' at " + t.getLine() + ":" + t.getCol()
|
||||
"期望的词素是'" + lexeme + "',但得到的是'" + t.getLexeme() +
|
||||
"在" + t.getLine() + ":" + t.getCol()
|
||||
);
|
||||
}
|
||||
return next();
|
||||
@ -122,8 +122,8 @@ public class TokenStream {
|
||||
Token t = peek();
|
||||
if (t.getType() != type) {
|
||||
throw new ParseException(
|
||||
"Expected token type " + type + " but got " + t.getType() +
|
||||
" ('" + t.getLexeme() + "') at " + t.getLine() + ":" + t.getCol()
|
||||
"期望的标记类型为 " + type + " 但实际得到的是 " + t.getType() +
|
||||
" ('" + t.getLexeme() + "') 在 " + t.getLine() + ":" + t.getCol()
|
||||
);
|
||||
}
|
||||
return next();
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package org.jcnc.snow.compiler.parser.context;
|
||||
|
||||
/**
|
||||
* 当解析过程中遇到意料之外或无法识别的 Token 时抛出。
|
||||
*/
|
||||
public final class UnexpectedToken extends ParseException {
|
||||
|
||||
public UnexpectedToken(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package org.jcnc.snow.compiler.parser.context;
|
||||
|
||||
/**
|
||||
* 当源码使用了当前编译器尚未支持的语言特性或语法时抛出。
|
||||
*/
|
||||
public final class UnsupportedFeature extends ParseException {
|
||||
|
||||
public UnsupportedFeature(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -1,24 +1,58 @@
|
||||
package org.jcnc.snow.compiler.parser.core;
|
||||
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
import org.jcnc.snow.compiler.parser.base.TopLevelParser;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.factory.TopLevelParserFactory;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* 语法解析引擎(ParserEngine)。
|
||||
* <p>
|
||||
* 负责驱动 Snow 源码的顶层语法结构解析,将源码 TokenStream
|
||||
* 递交给各类 TopLevelParser,并收集语法树节点与异常。
|
||||
* 支持容错解析,能够批量报告所有语法错误,并提供同步恢复功能。
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* 典型用法:
|
||||
* <pre>
|
||||
* ParserEngine engine = new ParserEngine(context);
|
||||
* List<Node> ast = engine.parse();
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @param ctx 解析器上下文,负责持有 TokenStream 及所有全局状态。
|
||||
*/
|
||||
public record ParserEngine(ParserContext ctx) {
|
||||
|
||||
/**
|
||||
* 解析输入 TokenStream,生成语法树节点列表。
|
||||
*
|
||||
* <p>
|
||||
* 调用各类顶级语句解析器(如 module, func, import),
|
||||
* 遇到错误时会自动跳过到下一行或已知结构关键字,继续后续分析,
|
||||
* 最终汇总所有错误。如果解析出现错误,将以
|
||||
* {@link UnexpectedToken} 抛出所有语法错误信息。
|
||||
* </p>
|
||||
*
|
||||
* @return AST 节点列表,每个节点对应一个顶层语法结构
|
||||
* @throws UnexpectedToken 如果解析期间发现语法错误
|
||||
*/
|
||||
public List<Node> parse() {
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
List<String> errs = new ArrayList<>();
|
||||
TokenStream ts = ctx.getTokens();
|
||||
|
||||
// 主循环:直到全部 token 处理完毕
|
||||
while (ts.isAtEnd()) {
|
||||
// 跳过空行
|
||||
// 跳过所有空行
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
continue;
|
||||
@ -30,22 +64,31 @@ public record ParserEngine(ParserContext ctx) {
|
||||
nodes.add(parser.parse(ctx));
|
||||
} catch (Exception ex) {
|
||||
errs.add(ex.getMessage());
|
||||
synchronize(ts); // 错误恢复
|
||||
synchronize(ts); // 错误恢复:同步到下一个语句
|
||||
}
|
||||
}
|
||||
|
||||
// 批量报告所有解析错误
|
||||
if (!errs.isEmpty()) {
|
||||
throw new IllegalStateException("解析过程中检测到 "
|
||||
+ errs.size() + " 处错误:\n - "
|
||||
+ String.join("\n - ", errs));
|
||||
StringJoiner sj = new StringJoiner("\n - ", "", "");
|
||||
errs.forEach(sj::add);
|
||||
throw new UnexpectedToken("解析过程中检测到 "
|
||||
+ errs.size() + " 处错误:\n - " + sj);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误同步:跳到下一行或下一个已注册顶层关键字
|
||||
* 错误同步机制:跳过当前 TokenStream,直到遇到下一行
|
||||
* 或下一个可识别的顶级结构关键字,以保证后续解析不会被卡住。
|
||||
* <p>
|
||||
* 同时会跳过连续空行。
|
||||
* </p>
|
||||
*
|
||||
* @param ts 当前 TokenStream
|
||||
*/
|
||||
private void synchronize(TokenStream ts) {
|
||||
// 跳到下一行或下一个顶层结构关键字
|
||||
while (ts.isAtEnd()) {
|
||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
@ -56,7 +99,7 @@ public record ParserEngine(ParserContext ctx) {
|
||||
}
|
||||
ts.next();
|
||||
}
|
||||
// 连续空行全部吃掉
|
||||
// 吃掉后续所有空行
|
||||
while (ts.isAtEnd() && ts.peek().getType() == TokenType.NEWLINE) {
|
||||
ts.next();
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import org.jcnc.snow.compiler.lexer.token.Token;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.context.UnsupportedFeature;
|
||||
import org.jcnc.snow.compiler.parser.expression.base.ExpressionParser;
|
||||
import org.jcnc.snow.compiler.parser.expression.base.InfixParselet;
|
||||
import org.jcnc.snow.compiler.parser.expression.base.PrefixParselet;
|
||||
@ -87,7 +88,7 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
Token token = ctx.getTokens().next();
|
||||
PrefixParselet prefix = prefixes.get(token.getType().name());
|
||||
if (prefix == null) {
|
||||
throw new IllegalStateException("没有为该 Token 类型注册前缀解析器: " + token.getType());
|
||||
throw new UnsupportedFeature("没有为该 Token 类型注册前缀解析器: " + token.getType());
|
||||
}
|
||||
|
||||
ExpressionNode left = prefix.parse(ctx, token);
|
||||
@ -96,7 +97,10 @@ public class PrattExpressionParser implements ExpressionParser {
|
||||
&& prec.ordinal() < nextPrecedence(ctx)) {
|
||||
String lex = ctx.getTokens().peek().getLexeme();
|
||||
InfixParselet infix = infixes.get(lex);
|
||||
if (infix == null) break;
|
||||
if (infix == null) {
|
||||
throw new UnsupportedFeature(
|
||||
"没有为该 Token 类型注册中缀解析器: " + token.getType());
|
||||
}
|
||||
left = infix.parse(ctx, left);
|
||||
}
|
||||
return left;
|
||||
|
||||
@ -7,6 +7,7 @@ import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.ast.ImportNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.ModuleNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.FunctionNode;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.function.FunctionParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -33,7 +34,7 @@ public class ModuleParser implements TopLevelParser {
|
||||
*
|
||||
* @param ctx 当前解析器上下文,包含词法流、状态信息等。
|
||||
* @return 返回一个 {@link ModuleNode} 实例,表示完整模块的语法结构。
|
||||
* @throws IllegalStateException 当模块体中出现未识别的语句时抛出。
|
||||
* @throws UnexpectedToken 当模块体中出现未识别的语句时抛出。
|
||||
*/
|
||||
@Override
|
||||
public ModuleNode parse(ParserContext ctx) {
|
||||
@ -86,7 +87,7 @@ public class ModuleParser implements TopLevelParser {
|
||||
functions.add(funcParser.parse(ctx));
|
||||
} else {
|
||||
// 遇到无法识别的语句开头,抛出异常并提供详细提示
|
||||
throw new IllegalStateException("Unexpected token in module: " + lex);
|
||||
throw new UnexpectedToken("Unexpected token in module: " + lex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import org.jcnc.snow.compiler.parser.ast.ExpressionStatementNode;
|
||||
import org.jcnc.snow.compiler.parser.ast.base.StatementNode;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
import org.jcnc.snow.compiler.parser.expression.PrattExpressionParser;
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ public class ExpressionStatementParser implements StatementParser {
|
||||
*
|
||||
* @param ctx 当前解析上下文,提供词法流与状态信息。
|
||||
* @return 返回 {@link AssignmentNode} 或 {@link ExpressionStatementNode} 表示的语法节点。
|
||||
* @throws IllegalStateException 若表达式起始为关键字或语法非法。
|
||||
* @throws UnexpectedToken 若表达式起始为关键字或语法非法。
|
||||
*/
|
||||
@Override
|
||||
public StatementNode parse(ParserContext ctx) {
|
||||
@ -47,7 +48,7 @@ public class ExpressionStatementParser implements StatementParser {
|
||||
|
||||
// 快速检查:若遇空行或关键字开头,不可作为表达式语句
|
||||
if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
|
||||
throw new IllegalStateException("Cannot parse expression starting with keyword: " + ts.peek().getLexeme());
|
||||
throw new UnexpectedToken("无法解析以关键字开头的表达式: " + ts.peek().getLexeme());
|
||||
}
|
||||
|
||||
// 获取当前 token 的行号、列号和文件名
|
||||
|
||||
@ -19,7 +19,6 @@ public class ScriptTopLevelParser implements TopLevelParser {
|
||||
public Node parse(ParserContext ctx) {
|
||||
String first = ctx.getTokens().peek().getLexeme();
|
||||
StatementParser sp = StatementParserFactory.get(first);
|
||||
StatementNode stmt = sp.parse(ctx);
|
||||
return stmt; // StatementNode 亦是 Node
|
||||
return sp.parse(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package org.jcnc.snow.compiler.parser.utils;
|
||||
import org.jcnc.snow.compiler.lexer.token.TokenType;
|
||||
import org.jcnc.snow.compiler.parser.context.ParserContext;
|
||||
import org.jcnc.snow.compiler.parser.context.TokenStream;
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
@ -45,7 +46,7 @@ public class FlexibleSectionParser {
|
||||
* @param ctx 当前解析上下文,提供语法环境与作用域信息
|
||||
* @param tokens 当前 token 流
|
||||
* @param sectionDefinitions 各个区块的定义映射(key 为关键字,value 为判断 + 解析逻辑组合)
|
||||
* @throws RuntimeException 若出现无法识别的关键字或未满足的匹配条件
|
||||
* @throws UnexpectedToken 若出现无法识别的关键字或未满足的匹配条件
|
||||
*/
|
||||
public static void parse(ParserContext ctx,
|
||||
TokenStream tokens,
|
||||
@ -70,7 +71,7 @@ public class FlexibleSectionParser {
|
||||
if (definition != null && definition.condition().test(tokens)) {
|
||||
definition.parser().accept(ctx, tokens); // 执行解析逻辑
|
||||
} else {
|
||||
throw new RuntimeException("未识别的关键字或条件不满足: " + keyword);
|
||||
throw new UnexpectedToken("未识别的关键字或条件不满足: " + keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package org.jcnc.snow.compiler.parser.utils;
|
||||
|
||||
import org.jcnc.snow.compiler.parser.context.UnexpectedToken;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@ -10,26 +12,28 @@ import java.util.Map.Entry;
|
||||
* - 序列化:将 Java 原生对象转换为符合 JSON 标准的字符串
|
||||
* <p>
|
||||
* 设计要点:
|
||||
* 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
|
||||
* 2. 解析器内部使用 char[] 缓冲区,提高访问性能
|
||||
* 3. 维护行列号信息,抛出异常时能精确定位错误位置
|
||||
* 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
|
||||
* 1. 使用静态方法作为唯一入口,避免状态共享导致的线程安全问题
|
||||
* 2. 解析器内部使用 char[] 缓冲区,提高访问性能
|
||||
* 3. 维护行列号信息,抛出异常时能精确定位错误位置
|
||||
* 4. 序列化器基于 StringBuilder,预分配容量,减少中间字符串创建
|
||||
*/
|
||||
public class JSONParser {
|
||||
|
||||
private JSONParser() {}
|
||||
private JSONParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JSON 文本解析为对应的 Java 对象
|
||||
*
|
||||
* @param input JSON 格式字符串
|
||||
* @return 对应的 Java 原生对象:
|
||||
* - JSON 对象 -> Map<String, Object>
|
||||
* - JSON 数组 -> List<Object>
|
||||
* - JSON 字符串 -> String
|
||||
* - JSON 数值 -> Long 或 Double
|
||||
* - JSON 布尔 -> Boolean
|
||||
* - JSON null -> null
|
||||
* @throws RuntimeException 如果遇到语法错误或多余字符,异常消息中包含行列信息
|
||||
* - JSON 对象 -> Map<String, Object>
|
||||
* - JSON 数组 -> List<Object>
|
||||
* - JSON 字符串 -> String
|
||||
* - JSON 数值 -> Long 或 Double
|
||||
* - JSON 布尔 -> Boolean
|
||||
* - JSON null -> null
|
||||
* @throws UnexpectedToken 如果遇到语法错误或多余字符,异常消息中包含行列信息
|
||||
*/
|
||||
public static Object parse(String input) {
|
||||
return new Parser(input).parseInternal();
|
||||
@ -37,6 +41,7 @@ public class JSONParser {
|
||||
|
||||
/**
|
||||
* 将 Java 原生对象序列化为 JSON 字符串
|
||||
*
|
||||
* @param obj 支持的类型:Map、Collection、String、Number、Boolean 或 null
|
||||
* @return 符合 JSON 规范的字符串
|
||||
*/
|
||||
@ -45,21 +50,31 @@ public class JSONParser {
|
||||
}
|
||||
|
||||
// ======= 内部解析器 =======
|
||||
|
||||
/**
|
||||
* 负责将 char[] 缓冲区中的 JSON 文本解析为 Java 对象
|
||||
*/
|
||||
private static class Parser {
|
||||
/** 输入缓冲区 */
|
||||
/**
|
||||
* 输入缓冲区
|
||||
*/
|
||||
private final char[] buf;
|
||||
/** 当前解析到的位置索引 */
|
||||
/**
|
||||
* 当前解析到的位置索引
|
||||
*/
|
||||
private int pos;
|
||||
/** 当前字符所在行号,从 1 开始 */
|
||||
/**
|
||||
* 当前字符所在行号,从 1 开始
|
||||
*/
|
||||
private int line;
|
||||
/** 当前字符所在列号,从 1 开始 */
|
||||
/**
|
||||
* 当前字符所在列号,从 1 开始
|
||||
*/
|
||||
private int col;
|
||||
|
||||
/**
|
||||
* 构造解析器,初始化缓冲区和行列信息
|
||||
*
|
||||
* @param input 待解析的 JSON 文本
|
||||
*/
|
||||
Parser(String input) {
|
||||
@ -115,7 +130,9 @@ public class JSONParser {
|
||||
while (true) {
|
||||
skipWhitespace();
|
||||
String key = parseString(); // 解析键
|
||||
skipWhitespace(); expect(':'); skipWhitespace();
|
||||
skipWhitespace();
|
||||
expect(':');
|
||||
skipWhitespace();
|
||||
Object val = parseValue(); // 解析值
|
||||
map.put(key, val);
|
||||
skipWhitespace();
|
||||
@ -123,7 +140,8 @@ public class JSONParser {
|
||||
advance(); // 跳过 '}'
|
||||
break;
|
||||
}
|
||||
expect(','); skipWhitespace();
|
||||
expect(',');
|
||||
skipWhitespace();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@ -149,7 +167,8 @@ public class JSONParser {
|
||||
advance();
|
||||
break;
|
||||
}
|
||||
expect(','); skipWhitespace();
|
||||
expect(',');
|
||||
skipWhitespace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@ -170,18 +189,35 @@ public class JSONParser {
|
||||
advance(); // 跳过 '\'
|
||||
c = currentChar();
|
||||
switch (c) {
|
||||
case '"': sb.append('"'); break;
|
||||
case '\\': sb.append('\\'); break;
|
||||
case '/': sb.append('/'); break;
|
||||
case 'b': sb.append('\b'); break;
|
||||
case 'f': sb.append('\f'); break;
|
||||
case 'n': sb.append('\n'); break;
|
||||
case 'r': sb.append('\r'); break;
|
||||
case 't': sb.append('\t'); break;
|
||||
case '"':
|
||||
sb.append('"');
|
||||
break;
|
||||
case '\\':
|
||||
sb.append('\\');
|
||||
break;
|
||||
case '/':
|
||||
sb.append('/');
|
||||
break;
|
||||
case 'b':
|
||||
sb.append('\b');
|
||||
break;
|
||||
case 'f':
|
||||
sb.append('\f');
|
||||
break;
|
||||
case 'n':
|
||||
sb.append('\n');
|
||||
break;
|
||||
case 'r':
|
||||
sb.append('\r');
|
||||
break;
|
||||
case 't':
|
||||
sb.append('\t');
|
||||
break;
|
||||
case 'u': // 解析 Unicode 转义
|
||||
String hex = new String(buf, pos+1, 4);
|
||||
String hex = new String(buf, pos + 1, 4);
|
||||
sb.append((char) Integer.parseInt(hex, 16));
|
||||
pos += 4; col += 4;
|
||||
pos += 4;
|
||||
col += 4;
|
||||
break;
|
||||
default:
|
||||
error("无效转义字符 '\\" + c + "'");
|
||||
@ -250,7 +286,8 @@ public class JSONParser {
|
||||
private void advance() {
|
||||
if (pos < buf.length) {
|
||||
if (buf[pos] == '\n') {
|
||||
line++; col = 1;
|
||||
line++;
|
||||
col = 1;
|
||||
} else {
|
||||
col++;
|
||||
}
|
||||
@ -292,16 +329,19 @@ public class JSONParser {
|
||||
* 抛出带行列定位的解析错误
|
||||
*/
|
||||
private void error(String msg) {
|
||||
throw new RuntimeException("Error at line " + line + ", column " + col + ": " + msg);
|
||||
throw new UnexpectedToken("在第 " + line + " 行,第 " + col + " 列出现错误: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
// ======= 内部序列化器 =======
|
||||
|
||||
/**
|
||||
* 负责高效地将 Java 对象写为 JSON 文本
|
||||
*/
|
||||
private static class Writer {
|
||||
/** 默认 StringBuilder 初始容量,避免频繁扩容 */
|
||||
/**
|
||||
* 默认 StringBuilder 初始容量,避免频繁扩容
|
||||
*/
|
||||
private static final int DEFAULT_CAPACITY = 1024;
|
||||
|
||||
/**
|
||||
@ -344,8 +384,8 @@ public class JSONParser {
|
||||
}
|
||||
sb.append(']');
|
||||
} else {
|
||||
// 其他类型,使用 toString 并加引号
|
||||
quote(obj.toString(), sb);
|
||||
throw new UnsupportedOperationException(
|
||||
"不支持的 JSON 字符串化类型: " + obj.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,12 +396,23 @@ public class JSONParser {
|
||||
sb.append('"');
|
||||
for (char c : s.toCharArray()) {
|
||||
switch (c) {
|
||||
case '\\': sb.append("\\\\"); break;
|
||||
case '"': sb.append("\\\""); break;
|
||||
case '\n': sb.append("\\n"); break;
|
||||
case '\r': sb.append("\\r"); break;
|
||||
case '\t': sb.append("\\t"); break;
|
||||
default: sb.append(c);
|
||||
case '\\':
|
||||
sb.append("\\\\");
|
||||
break;
|
||||
case '"':
|
||||
sb.append("\\\"");
|
||||
break;
|
||||
case '\n':
|
||||
sb.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
sb.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
sb.append("\\t");
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
sb.append('"');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user