开年以来项目上一直在忙。。。一直没分享啥有意思的东西,前端时间在社区看到关于json工具的讨论。我们也有相应的烦恼,以前非常喜欢使用FastJson,API使用简洁速度还快。但是出了几档子事儿之后,我们有Y企客户,扫描到该jar后,进行了相关处理。以及后面又使用了HuToolJson等工具。再后来新项目上干脆自己撸了一个json字符串解析工具,因为我们json字符串都比较小,就是表单对象或者是有数组对象的表单对象业务,非常简单,不需要使用什么反射之类的业务。
算了废话不多说了,上马吧:
创建一个 MyJson,因为JsonKit 是JF内置的工具类,所以起个MyJson名。
该工具没有实现JF内置的Json接口,因为toJson方法JF的JFinalJson已经非常OK,只是parse未实现。但是parse方法参数是parse(String jsonString, Class<T> type) Class并不是我们想要的,我们常见就 KV对象,或者 Record 对象,list对象即可了。
反射Class实现起来也麻烦,最主要的是目前用不上。 所以干脆写个独立的工具类即可了。
import com.jfinal.kit.Kv;
import com.jfinal.plugin.activerecord.Record;
import java.util.ArrayList;
/**
* 简易JSON字符串解析工具类 V1.1.3
* @author 杜福忠
*/
@SuppressWarnings("unused")
public class MyJson {
private String jsonStr;
private int index;
private boolean kvToRecord = false;
public static MyJson getJson() {
return new MyJson();
}
public Object parse(String jsonString) {
this.jsonStr = jsonString.trim();
this.index = 0;
return parseValue();
}
public static Kv parseKv(String jsonString) {
return (Kv) getJson().parse(jsonString);
}
@SuppressWarnings("unchecked")
public static ArrayList<Object> parseList(String jsonString) {
return (ArrayList<Object>) getJson().parse(jsonString);
}
public static Record parseRecord(String jsonString) {
return (Record) getJson().setKvToRecord(true).parse(jsonString);
}
@SuppressWarnings("unchecked")
public static ArrayList<Record> parseRecordList(String jsonString) {
return (ArrayList<Record>) getJson().setKvToRecord(true).parse(jsonString);
}
public MyJson setKvToRecord(boolean kvToRecord) {
this.kvToRecord = kvToRecord;
return this;
}
private Object parseValue() {
skipWhitespace();
char current = jsonStr.charAt(index);
if (current == '{') return parseKv();
else if (current == '[') return parseList();
else if (current == '"') return parseString();
else if (current == '-' || Character.isDigit(current)) return parseNumber();
else if (current == 't' || current == 'f' || current == 'T' || current == 'F') return parseBoolean();
else if (current == 'n' || current == 'N') return parseNull();
else throw errorMsg(index, "未知符号:" + current);
}
private void skipWhitespace() {
while (index < jsonStr.length() && Character.isWhitespace(jsonStr.charAt(index))){
index++;
}
}
private StringBuilder parseString_sb;
private String parseString() {
if (parseString_sb == null){
parseString_sb = new StringBuilder();
}
index++; // 跳过开头引号
while (index < jsonStr.length()) {
char c = jsonStr.charAt(index++);
if (c == '"') break;
else if (c == '\\') {
c = jsonStr.charAt(index++);
parseString_sb.append(c); // 简化处理转义符(如 \" → ")
} else parseString_sb.append(c);
}
String ret = parseString_sb.toString();
parseString_sb.setLength(0);
return ret;
}
private Object parseKv() {
Object kv = kvToRecord ? new Record() : new Kv();
index++; // 跳过 '{'
while (true) {
skipWhitespace();
if (jsonStr.charAt(index) == '}') {
index++;
return kv;
}
String key = parseString();
skipWhitespace();
if (jsonStr.charAt(index++) != ':') {
throw errorMsg(index, "没找到对象间隔符,应该是':'符号");
}
Object value = parseValue();
if (kv instanceof Kv) {
((Kv)kv).set(key, value);
}else{
((Record)kv).set(key, value);
}
skipWhitespace();
if (jsonStr.charAt(index) == ',') {
index++;
}else if (jsonStr.charAt(index) != '}') {
String msg = "没找到对象结尾符,推测应该是','或者'}'符号";
if (value instanceof Number && jsonStr.charAt(index + 1) == '}'){
msg = "导致数字格式不能识别";
}
throw errorMsg(index, msg);
}
}
}
private ArrayList<Object> parseList() {
ArrayList<Object> list = new ArrayList<>();
index++; // 跳过 '['
while (true) {
skipWhitespace();
if (jsonStr.charAt(index) == ']') {
index++;
return list;
}
Object value = parseValue();
list.add(value);
skipWhitespace();
if (jsonStr.charAt(index) == ','){
index++;
}else if (jsonStr.charAt(index) != ']'){
String msg = "没找到数组结尾符,应该是','或者']'符号";
if (value instanceof Number && jsonStr.charAt(index + 1) == ']'){
msg = "导致数字格式不能识别";
}
throw errorMsg(index, msg);
}
}
}
private Object parseNull() {
if (jsonStr.startsWith("null", index) || jsonStr.startsWith("NULL", index)) {
index += 4;
return null;
}
throw errorMsg(index, "无效的null值");
}
private Boolean parseBoolean() {
if (jsonStr.startsWith("true", index) || jsonStr.startsWith("TRUE", index)) {
index += 4;
return Boolean.TRUE;
} else if (jsonStr.startsWith("false", index) || jsonStr.startsWith("FALSE", index)) {
index += 5;
return Boolean.FALSE;
}
throw errorMsg(index, "无效的布尔值true、false");
}
private Number parseNumber() {
int start = index;
boolean isDouble = false;
while (index < jsonStr.length()) {
char ch = jsonStr.charAt(index);
if (Character.isDigit(ch)){
index++;
continue;
}
if (ch == '.' || ch == '-' || ch == '+' || ch == 'E' || ch == 'e'){
index++;
isDouble = true;
continue;
}
break;
}
String numStr = jsonStr.substring(start, index);
if (isDouble) return Double.parseDouble(numStr);
else return Long.parseLong(numStr);
}
private RuntimeException errorMsg(int index, String msg){
return new RuntimeException("JSON字符串解析错误:角标=" + index
+ ";内容‘" + errorStr(index + 5, 15)//
+ "’里面的字符 ‘" + errorStr(index + 1, 1) +"’ "+ msg);
}
private String errorStr(int index, int len){
int start = Math.max(index - len, 0);
int end = Math.min(index, jsonStr.length());
return jsonStr.substring(start, end);
}
}可以看到有几个静态方法:
public static Kv parseKv(String jsonString) public static ArrayList<Object> parseList(String jsonString) public static Record parseRecord(String jsonString) public static ArrayList<Record> parseRecordList(String jsonString)
键值对象使用Kv对象是非常的棒,Kv对象自带数据类型的转换,kv.getInt > getStr >getDate 等等方法,Record也是一样自带数据类型转换。所以可以使得json解析方法非常简单,不用关注太多类型转换问题。
下面展示一个Kv用法例子:
public static void main(String[] args) {
String jsonStr = "{ \"name\": \"John\", \"age\": 30, \"list\": [90, 85, 71], \"obj\": { \"name\": \"John\", \"age\": 30}}";
Kv kv = MyJson.parseKv(jsonStr);
Integer age = kv.getInt("age");
System.out.println(age);
List<Number> list = kv.getAs("list");
for (Number x : list) {
System.out.println(x);
}
Kv obj = kv.getAs("obj");
System.out.println(obj.getStr("name"));
}可以看到 kv.getAs 获取对象或者集合都非常方便。
特别是泛型上面的处理使用比FastJson要方便很多。在普通json字符串对象的解析上性能也是嗷嗷领先,毕竟代码少功能还简单,执行自然快。。。
Record 也是一样:
public static void main(String[] args) {
String jsonStr = "{ \"name\": \"John\", \"age\": 30, \"list\": [90, 85, 71], \"obj\": { \"name\": \"John\", \"age\": 30}}";
Record r = MyJson.parseRecord(jsonStr);
Integer age = r.getInt("age");
System.out.println(age);
List<Number> list = r.get("list");
for (Number x : list) {
System.out.println(x);
}
Record obj = r.get("obj");
System.out.println(obj.getStr("name"));
}入数据库的业务,都不用map转换对象了,直接用,非常方便。
好了,分享到此结束!只适合部分业务场景。
如果需要反射类的业务处理,也可以自行改造一下支持。可参考我之前分享的一个例子:
Record转JavaBean以及List转JavaBea
划水篇~
2025-07-30 更新功能:错误时 友好提示错误位置内容和不能识别的字符。