JFinal使用技巧-百行代码实现JSON字符串解析工具类(无三方依赖)

开年以来项目上一直在忙。。。一直没分享啥有意思的东西,前端时间在社区看到关于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<Long> list = kv.getAs("list");
    for (Long 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<Long> list = r.get("list");
    for (Long 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 更新功能:错误时 友好提示错误位置内容和不能识别的字符。

评论区

JFinal

2025-06-15 16:22

实现非常简单,先收藏,后面有用

北流家园网

2025-06-16 14:52

杜总出品,必属精品,收藏

水滴鱼

2025-06-18 15:38

zzutligang

2025-06-25 17:51

点赞

fmpoffice

2025-06-28 13:49

杜总出品必须精品

热门分享

扫码入社