Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自己动手实现一个简单的JSON解析器

自己动手实现一个简单的JSON解析器

原创
作者头像
田小波
修改于 2018-04-29 02:09:28
修改于 2018-04-29 02:09:28
4.1K15
举报

1. 背景

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。相对于另一种数据交换格式 XML,JSON 有着诸多优点。比如易读性更好,占用空间更少等。在 web 应用开发领域内,得益于 JavaScript 对 JSON 提供的良好支持,JSON 要比 XML 更受开发人员青睐。所以作为开发人员,如果有兴趣的话,还是应该深入了解一下 JSON 相关的知识。本着探究 JSON 原理的目的,我将会在这篇文章中详细向大家介绍一个简单的JSON解析器的解析流程和实现细节。由于 JSON 本身比较简单,解析起来也并不复杂。所以如果大家感兴趣的话,在看完本文后,不妨自己动手实现一个 JSON 解析器。好了,其他的话就不多说了,接下来让我们移步到重点章节吧。

2. JSON 解析器实现原理

JSON 解析器从本质上来说就是根据 JSON 文法规则创建的状态机,输入是一个 JSON 字符串,输出是一个 JSON 对象。一般来说,解析过程包括词法分析和语法分析两个阶段。词法分析阶段的目标是按照构词规则将 JSON 字符串解析成 Token 流,比如有如下的 JSON 字符串:

代码语言:txt
AI代码解释
复制
{
    "name" : "小明",
    "age": 18
}

结果词法分析后,得到一组 Token,如下:

{name:小明,age:18}

图1 词法分析器输入输出

词法分析解析出 Token 序列后,接下来要进行语法分析。语法分析的目的是根据 JSON 文法检查上面 Token 序列所构成的 JSON 结构是否合法。比如 JSON 文法要求非空 JSON 对象以键值对的形式出现,形如 object = {string : value}。如果传入了一个格式错误的字符串,比如

代码语言:txt
AI代码解释
复制
{
    "name", "小明"
}

那么在语法分析阶段,语法分析器分析完 Token name后,认为它是一个符合规则的 Token,并且认为它是一个键。接下来,语法分析器读取下一个 Token,期望这个 Token 是 :。但当它读取了这个 Token,发现这个 Token 是 ,,并非其期望的:,于是文法分析器就会报错误。

图2 语法分析器输入输出

这里简单总结一下上面两个流程,词法分析是将字符串解析成一组 Token 序列,而语法分析则是检查输入的 Token 序列所构成的 JSON 格式是否合法。这里大家对 JSON 的解析流程有个印象就好,接下来我会详细分析每个流程。

2.1 词法分析

在本章开始,我说了词法解析的目的,即按照“构词规则”将 JSON 字符串解析成 Token 流。请注意双引号引起来词--构词规则,所谓构词规则是指词法分析模块在将字符串解析成 Token 时所参考的规则。在 JSON 中,构词规则对应于几种数据类型,当词法解析器读入某个词,且这个词类型符合 JSON 所规定的数据类型时,词法分析器认为这个词符合构词规则,就会生成相应的 Token。这里我们可以参考http://www.json.org/对 JSON 的定义,罗列一下 JSON 所规定的数据类型:

  • BEGIN_OBJECT({)
  • END_OBJECT(})
  • BEGIN_ARRAY([)
  • END_ARRAY(])
  • NULL(null)
  • NUMBER(数字)
  • STRING(字符串)
  • BOOLEAN(true/false)
  • SEP_COLON(:)
  • SEP_COMMA(,)

当词法分析器读取的词是上面类型中的一种时,即可将其解析成一个 Token。我们可以定义一个枚举类来表示上面的数据类型,如下:

代码语言:txt
AI代码解释
复制
public enum TokenType {
    BEGIN_OBJECT(1),
    END_OBJECT(2),
    BEGIN_ARRAY(4),
    END_ARRAY(8),
    NULL(16),
    NUMBER(32),
    STRING(64),
    BOOLEAN(128),
    SEP_COLON(256),
    SEP_COMMA(512),
    END_DOCUMENT(1024);

    TokenType(int code) {
        this.code = code;
    }

    private int code;

    public int getTokenCode() {
        return code;
    }
}

在解析过程中,仅有 TokenType 类型还不行。我们除了要将某个词的类型保存起来,还需要保存这个词的字面量。所以,所以这里还需要定义一个 Token 类。用于封装词类型和字面量,如下:

代码语言:txt
AI代码解释
复制
public class Token {
    private TokenType tokenType;
    private String value;
    // 省略不重要的代码
}

定义好了 Token 类,接下来再来定义一个读取字符串的类。如下:

代码语言:txt
AI代码解释
复制
public CharReader(Reader reader) {
        this.reader = reader;
        buffer = new char[BUFFER_SIZE];
    }

    /**
     * 返回 pos 下标处的字符,并返回
     * @return 
     * @throws IOException
     */
    public char peek() throws IOException {
        if (pos - 1 >= size) {
            return (char) -1;
        }

        return buffer[Math.max(0, pos - 1)];
    }

    /**
     * 返回 pos 下标处的字符,并将 pos + 1,最后返回字符
     * @return 
     * @throws IOException
     */
    public char next() throws IOException {
        if (!hasMore()) {
            return (char) -1;
        }

        return buffer[pos++];
    }

    public void back() {
        pos = Math.max(0, --pos);
    }

    public boolean hasMore() throws IOException {
        if (pos < size) {
            return true;
        }

        fillBuffer();
        return pos < size;
    }

    void fillBuffer() throws IOException {
        int n = reader.read(buffer);
        if (n == -1) {
            return;
        }

        pos = 0;
        size = n;
    }
}

有了 TokenType、Token 和 CharReader 这三个辅助类,接下来我们就可以实现词法解析器了。

代码语言:txt
AI代码解释
复制
public class Tokenizer {
    private CharReader charReader;
    private TokenList tokens;

    public TokenList tokenize(CharReader charReader) throws IOException {
        this.charReader = charReader;
        tokens = new TokenList();
        tokenize();

        return tokens;
    }

    private void tokenize() throws IOException {
        // 使用do-while处理空文件
        Token token;
        do {
            token = start();
            tokens.add(token);
        } while (token.getTokenType() != TokenType.END_DOCUMENT);
    }

    private Token start() throws IOException {
        char ch;
        for(;;) {
            if (!charReader.hasMore()) {
                return new Token(TokenType.END_DOCUMENT, null);
            }

            ch = charReader.next();
            if (!isWhiteSpace(ch)) {
                break;
            }
        }

        switch (ch) {
            case '{':
                return new Token(TokenType.BEGIN_OBJECT, String.valueOf(ch));
            case '}':
                return new Token(TokenType.END_OBJECT, String.valueOf(ch));
            case '[':
                return new Token(TokenType.BEGIN_ARRAY, String.valueOf(ch));
            case ']':
                return new Token(TokenType.END_ARRAY, String.valueOf(ch));
            case ',':
                return new Token(TokenType.SEP_COMMA, String.valueOf(ch));
            case ':':
                return new Token(TokenType.SEP_COLON, String.valueOf(ch));
            case 'n':
                return readNull();
            case 't':
            case 'f':
                return readBoolean();
            case '"':
                return readString();
            case '-':
                return readNumber();
        }

        if (isDigit(ch)) {
            return readNumber();
        }

        throw new JsonParseException("Illegal character");
    }
    
    private Token readNull() {...}
    private Token readBoolean() {...}
    private Token readString() {...}
    private Token readNumber() {...}
}

上面的代码是词法分析器的实现,部分代码这里没有贴出来,后面具体分析的时候再贴。先来看看词法分析器的核心方法 start,这个方法代码量不多,并不复杂。其通过一个死循环不停的读取字符,然后再根据字符的类型,执行不同的解析逻辑。上面说过,JSON 的解析过程比较简单。原因在于,在解析时,只需通过每个词第一个字符即可判断出这个词的 Token Type。比如:

  • 第一个字符是{}[],:,直接封装成相应的 Token 返回即可
  • 第一个字符是n,期望这个词是null,Token 类型是NULL
  • 第一个字符是tf,期望这个词是true或者false,Token 类型是 BOOLEAN
  • 第一个字符是",期望这个词是字符串,Token 类型为String
  • 第一个字符是0~9-,期望这个词是数字,类型为NUMBER

正如上面所说,词法分析器只需要根据每个词的第一个字符,即可知道接下来它所期望读取的到的内容是什么样的。如果满足期望了,则返回 Token,否则返回错误。下面就来看看词法解析器在碰到第一个字符是n"时的处理过程。先看碰到字符n的处理过程:

代码语言:txt
AI代码解释
复制
private Token readNull() throws IOException {
    if (!(charReader.next() == 'u' && charReader.next() == 'l' && charReader.next() == 'l')) {
        throw new JsonParseException("Invalid json string");
    }

    return new Token(TokenType.NULL, "null");
}

上面的代码很简单,词法分析器在读取字符n后,期望后面的三个字符分别是u,l,l,与 n 组成词 null。如果满足期望,则返回类型为 NULL 的 Token,否则报异常。readNull 方法逻辑很简单,不多说了。接下来看看 string 类型的数据处理过程:

代码语言:txt
AI代码解释
复制
private Token readString() throws IOException {
    StringBuilder sb = new StringBuilder();
    for (;;) {
        char ch = charReader.next();
        // 处理转义字符
        if (ch == '\\') {
            if (!isEscape()) {
                throw new JsonParseException("Invalid escape character");
            }
            sb.append('\\');
            ch = charReader.peek();
            sb.append(ch);
            // 处理 Unicode 编码,形如 \u4e2d。且只支持 \u0000 ~ \uFFFF 范围内的编码
            if (ch == 'u') {
                for (int i = 0; i < 4; i++) {
                    ch = charReader.next();
                    if (isHex(ch)) {
                        sb.append(ch);
                    } else {
                        throw new JsonParseException("Invalid character");
                    }
                }
            }
        } else if (ch == '"') {    // 碰到另一个双引号,则认为字符串解析结束,返回 Token
            return new Token(TokenType.STRING, sb.toString());
        } else if (ch == '\r' || ch == '\n') {    // 传入的 JSON 字符串不允许换行
            throw new JsonParseException("Invalid character");
        } else {
            sb.append(ch);
        }
    }
}

private boolean isEscape() throws IOException {
    char ch = charReader.next();
    return (ch == '"' || ch == '\\' || ch == 'u' || ch == 'r'
                || ch == 'n' || ch == 'b' || ch == 't' || ch == 'f');
}

private boolean isHex(char ch) {
    return ((ch >= '0' && ch <= '9') || ('a' <= ch && ch <= 'f')
            || ('A' <= ch && ch <= 'F'));
}

string 类型的数据解析起来要稍微复杂一些,主要是需要处理一些特殊类型的字符。JSON 所允许的特殊类型的字符如下:

\"

\\

\b

\f

\n

\r

\t

\u four-hex-digits

\/

最后一种特殊字符\/代码中未做处理,其他字符均做了判断,判断逻辑在 isEscape 方法中。在传入 JSON 字符串中,仅允许字符串包含上面所列的转义字符。如果乱传转义字符,解析时会报错。对于 STRING 类型的词,解析过程始于字符",也终于"。所以在解析的过程中,当再次遇到字符",readString 方法会认为本次的字符串解析过程结束,并返回相应类型的 Token。

上面说了 null 类型和 string 类型的数据解析过程,过程并不复杂,理解起来应该不难。至于 boolean 和 number 类型的数据解析过程,大家有兴趣的话可以自己看源码,这里就不在说了。

2.2 语法分析

当词法分析结束后,且分析过程中没有抛出错误,那么接下来就可以进行语法分析了。语法分析过程以词法分析阶段解析出的 Token 序列作为输入,输出 JSON Object 或 JSON Array。语法分析器的实现的文法如下:

代码语言:txt
AI代码解释
复制
object = {} | { members }
members = pair | pair , members
pair = string : value
array = [] | [ elements ]
elements = value  | value , elements
value = string | number | object | array | true | false | null

语法分析器的实现需要借助两个辅助类,也就是语法分析器的输出类,分别是 JsonObject 和 JsonArray。代码如下:

代码语言:txt
AI代码解释
复制
public class JsonObject {

    private Map<String, Object> map = new HashMap<String, Object>();

    public void put(String key, Object value) {
        map.put(key, value);
    }

    public Object get(String key) {
        return map.get(key);
    }

    public List<Map.Entry<String, Object>> getAllKeyValue() {
        return new ArrayList<>(map.entrySet());
    }

    public JsonObject getJsonObject(String key) {
        if (!map.containsKey(key)) {
            throw new IllegalArgumentException("Invalid key");
        }

        Object obj = map.get(key);
        if (!(obj instanceof JsonObject)) {
            throw new JsonTypeException("Type of value is not JsonObject");
        }

        return (JsonObject) obj;
    }

    public JsonArray getJsonArray(String key) {
        if (!map.containsKey(key)) {
            throw new IllegalArgumentException("Invalid key");
        }

        Object obj = map.get(key);
        if (!(obj instanceof JsonArray)) {
            throw new JsonTypeException("Type of value is not JsonArray");
        }

        return (JsonArray) obj;
    }

    @Override
    public String toString() {
        return BeautifyJsonUtils.beautify(this);
    }
}

public class JsonArray implements Iterable {

    private List list = new ArrayList();

    public void add(Object obj) {
        list.add(obj);
    }

    public Object get(int index) {
        return list.get(index);
    }

    public int size() {
        return list.size();
    }

    public JsonObject getJsonObject(int index) {
        Object obj = list.get(index);
        if (!(obj instanceof JsonObject)) {
            throw new JsonTypeException("Type of value is not JsonObject");
        }

        return (JsonObject) obj;
    }

    public JsonArray getJsonArray(int index) {
        Object obj = list.get(index);
        if (!(obj instanceof JsonArray)) {
            throw new JsonTypeException("Type of value is not JsonArray");
        }

        return (JsonArray) obj;
    }

    @Override
    public String toString() {
        return BeautifyJsonUtils.beautify(this);
    }

    public Iterator iterator() {
        return list.iterator();
    }
}

语法解析器的核心逻辑封装在了 parseJsonObject 和 parseJsonArray 两个方法中,接下来我会详细分析 parseJsonObject 方法,parseJsonArray 方法大家自己分析吧。parseJsonObject 方法实现如下:

代码语言:txt
AI代码解释
复制
private JsonObject parseJsonObject() {
    JsonObject jsonObject = new JsonObject();
    int expectToken = STRING_TOKEN | END_OBJECT_TOKEN;
    String key = null;
    Object value = null;
    while (tokens.hasMore()) {
        Token token = tokens.next();
        TokenType tokenType = token.getTokenType();
        String tokenValue = token.getValue();
        switch (tokenType) {
        case BEGIN_OBJECT:
            checkExpectToken(tokenType, expectToken);
            jsonObject.put(key, parseJsonObject());    // 递归解析 json object
            expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN;
            break;
        case END_OBJECT:
            checkExpectToken(tokenType, expectToken);
            return jsonObject;
        case BEGIN_ARRAY:    // 解析 json array
            checkExpectToken(tokenType, expectToken);
            jsonObject.put(key, parseJsonArray());
            expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN;
            break;
        case NULL:
            checkExpectToken(tokenType, expectToken);
            jsonObject.put(key, null);
            expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN;
            break;
        case NUMBER:
            checkExpectToken(tokenType, expectToken);
            if (tokenValue.contains(".") || tokenValue.contains("e") || tokenValue.contains("E")) {
                jsonObject.put(key, Double.valueOf(tokenValue));
            } else {
                Long num = Long.valueOf(tokenValue);
                if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) {
                    jsonObject.put(key, num);
                } else {
                    jsonObject.put(key, num.intValue());
                }
            }
            expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN;
            break;
        case BOOLEAN:
            checkExpectToken(tokenType, expectToken);
            jsonObject.put(key, Boolean.valueOf(token.getValue()));
            expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN;
            break;
        case STRING:
            checkExpectToken(tokenType, expectToken);
            Token preToken = tokens.peekPrevious();
            /*
             * 在 JSON 中,字符串既可以作为键,也可作为值。
             * 作为键时,只期待下一个 Token 类型为 SEP_COLON。
             * 作为值时,期待下一个 Token 类型为 SEP_COMMA 或 END_OBJECT
             */
            if (preToken.getTokenType() == TokenType.SEP_COLON) {
                value = token.getValue();
                jsonObject.put(key, value);
                expectToken = SEP_COMMA_TOKEN | END_OBJECT_TOKEN;
            } else {
                key = token.getValue();
                expectToken = SEP_COLON_TOKEN;
            }
            break;
        case SEP_COLON:
            checkExpectToken(tokenType, expectToken);
            expectToken = NULL_TOKEN | NUMBER_TOKEN | BOOLEAN_TOKEN | STRING_TOKEN
                    | BEGIN_OBJECT_TOKEN | BEGIN_ARRAY_TOKEN;
            break;
        case SEP_COMMA:
            checkExpectToken(tokenType, expectToken);
            expectToken = STRING_TOKEN;
            break;
        case END_DOCUMENT:
            checkExpectToken(tokenType, expectToken);
            return jsonObject;
        default:
            throw new JsonParseException("Unexpected Token.");
        }
    }

    throw new JsonParseException("Parse error, invalid Token.");
}

private void checkExpectToken(TokenType tokenType, int expectToken) {
    if ((tokenType.getTokenCode() & expectToken) == 0) {
        throw new JsonParseException("Parse error, invalid Token.");
    }
}

parseJsonObject 方法解析流程大致如下:

  1. 读取一个 Token,检查这个 Token 是否是其所期望的类型
  2. 如果是,更新期望的 Token 类型。否则,抛出异常,并退出
  3. 重复步骤1和2,直至所有的 Token 都解析完,或出现异常

上面的步骤并不复杂,但有可能不好理解。这里举个例子说明一下,有如下的 Token 序列:

{id:1}

parseJsonObject 解析完 { Token 后,接下来它将期待 STRING 类型的 Token 或者 END_OBJECT 类型的 Token 出现。于是 parseJsonObject 读取了一个新的 Token,发现这个 Token 的类型是 STRING 类型,满足期望。于是 parseJsonObject 更新期望Token 类型为 SEL_COLON,即:。如此循环下去,直至 Token 序列解析结束或者抛出异常退出。

上面的解析流程虽然不是很复杂,但在具体实现的过程中,还是需要注意一些细节问题。比如:

  1. 在 JSON 中,字符串既可以作为键,也可以作为值。作为键时,语法分析器期待下一个 Token 类型为 SEP_COLON。而作为值时,则期待下一个 Token 类型为 SEP_COMMA 或 END_OBJECT。所以这里要判断该字符串是作为键还是作为值,判断方法也比较简单,即判断上一个 Token 的类型即可。如果上一个 Token 是 SEP_COLON,即:,那么此处的字符串只能作为值了。否则,则只能做为键。
  2. 对于整数类型的 Token 进行解析时,简单点处理,可以直接将该整数解析成 Long 类型。但考虑到空间占用问题,对于 [Integer.MIN_VALUE, Integer.MAX_VALUE] 范围内的整数来说,解析成 Integer 更为合适,所以解析的过程中也需要注意一下。

3. 测试及效果展示

为了验证代码的正确性,这里对代码进行了简单的测试。测试数据来自网易音乐,大约有4.5W个字符。为了避免每次下载数据,因数据发生变化而导致测试不通过的问题。我将某一次下载的数据保存在了 music.json 文件中,后面每次测试都会从文件中读取数据。关于测试部分,这里就不贴代码和截图了。大家有兴趣的话,可以自己下载源码测试玩玩。

测试就不多说了,接下来看看 JSON 美化效果展示。这里随便模拟点数据,就模拟王者荣耀里的狄仁杰英雄信息吧(对,这个英雄我经常用)。如下图:

图3 JSON 美化结果

关于 JSON 美化的代码这里也不讲解了,并非重点,只算一个彩蛋吧。

4. 写作最后

到此,本文差不多要结束了。本文对应的代码已经放到了 github 上,需要的话,大家可自行下载。传送门 -> JSONParser。这里需要声明一下,本文对应的代码实现了一个比较简陋的 JSON 解析器,实现的目的是探究 JSON 的解析原理。JSONParser 只算是一个练习性质的项目,代码实现的并不优美,而且缺乏充足的测试。同时,限于本人的能力(编译原理基础基本可以忽略),我并无法保证本文以及对应的代码中不出现错误。如果大家在阅读代码的过程中,发现了一些错误,或者写的不好的地方,可以提出来,我来修改。如果这些错误对你造成了困扰,这里先说一声很抱歉。最后,本文及实现主要参考了一起写一个JSON解析器如何编写一个JSON解析器两篇文章及两篇文章对应的实现代码,在这里向着两篇博文的作者表示感谢。好了,本文到此结束,祝大家生生活愉快!再见。

参考

  1. 一起写一个JSON解析器
  2. 如何编写一个JSON解析器
  3. 介绍JSON
  4. 写一个 JSON、XML 或 YAML 的 Parser 的思路是什么?-- 知乎

本文在知识共享许可协议 4.0 下发布,转载需在明显位置处注明出处 作者:coolblog.xyz 本文同步发布在我的个人博客:http://www.coolblog.xyz

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
15 条评论
热度
最新
感谢分享!可以参考你的写法,自己实现一个其他的语言的解析器
感谢分享!可以参考你的写法,自己实现一个其他的语言的解析器
11点赞举报
?
?
回复回复点赞举报
写的很详细,对JSON有了新的认识
写的很详细,对JSON有了新的认识
回复回复点赞举报
很喜欢作者的文章,写的很详细,也学到了很多
很喜欢作者的文章,写的很详细,也学到了很多
11点赞举报
谢谢阅读。但愿没有写错的地方,以免误导了你们
谢谢阅读。但愿没有写错的地方,以免误导了你们
回复回复点赞举报
哇,你真是我少见的有评必回的大佬了
哇,你真是我少见的有评必回的大佬了
11点赞举报
这就尴尬了,我不是大佬。
这就尴尬了,我不是大佬。
回复回复点赞举报
感谢分享,赞一个
感谢分享,赞一个
11点赞举报
谢谢阅读
谢谢阅读
回复回复点赞举报
这个可以做成应用,直接把json转换成文本吗?
这个可以做成应用,直接把json转换成文本吗?
33点赞举报
不太明白你的意思,json 本身就是使用文本存储数据了
不太明白你的意思,json 本身就是使用文本存储数据了
回复回复点赞举报
可能是提取文本的内容?因为json不是有格式。提取出来再进行其他处理
可能是提取文本的内容?因为json不是有格式。提取出来再进行其他处理
回复回复点赞举报
查看全部3条回复
json解析会用到正则匹配吗
json解析会用到正则匹配吗
11点赞举报
据我所知不会用到,当然网上文章说正则可以匹配 JSON,但准确性我没验证。有兴趣的话,你可以自己上网查找资料验证一下
据我所知不会用到,当然网上文章说正则可以匹配 JSON,但准确性我没验证。有兴趣的话,你可以自己上网查找资料验证一下
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
徒手撸一个JSON解析器
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。相对于另一种数据交换格式 XML,JSON 有着诸多优点。比如易读性更好,占用空间更少等。在 web 应用开发领域内,得益于 JavaScript 对 JSON 提供的良好支持,JSON 要比 XML 更受开发人员青睐。所以作为开发人员,如果有兴趣的话,还是应该深入了解一下 JSON 相关的知识。
南风
2019/12/18
8290
撸一个 JSON 解析器
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。
芋道源码
2019/05/13
1K0
实现一个JSON解析器,有那么难吗?
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。
Java团长
2019/03/06
1.3K0
实现一个JSON解析器,有那么难吗?
如何编写一个JSON解析器
编写一个JSON解析器实际上就是一个函数,它的输入是一个表示JSON的字符串,输出是结构化的对应到语言本身的数据结构。
bear_fish
2018/09/20
9730
如何编写一个JSON解析器
Antlr实战之JSON解析器slowjson
最近一直在学习编译原理,然后就了解到了antlr4这个强大的工具,antlr的全称是(Another Tool for Language Recognition),是一款很强大的词法和语法分析工具,虽然是用java写成的,但它也能生成c++、go……等语言的代码。它的主要作用就是你可以用巴科斯范式来描述语法规则,然后它帮你生成对应的解析器。
xindoo
2021/01/22
1.5K0
Antlr实战之JSON解析器slowjson
高性能Java解析器实现过程详解
高性能Java解析器实现过程详解 如果你没有指定数据或语言标准的或开源的Java解析器, 可能经常要用Java实现你自己的数据或语言解析器。或者,可能有很多解析器可选,但是要么太慢,要么太耗内存,或者没有你需要的特定功能。或者开源解析器存在缺陷,或者开源解析器项目被取消诸如此类原因。上述原因都没有你将需要实现你自己的解析器的事实重要。 当你必需实现自己的解析器时,你会希望它有良好表现,灵活,功能丰富,易于使用,最后但更重要是易于实现,毕竟你的名字会出现在代码中。本文中,我将介绍一种用Java实现高性能解析器
用户1289394
2018/02/27
2.4K0
高性能Java解析器实现过程详解
如何实现一个SQL解析器
随着技术的不断的发展,在大数据领域出现了越来越多的技术框架。而为了降低大数据的学习成本和难度,越来越多的大数据技术和应用开始支持SQL进行数据查询。SQL作为一个学习成本很低的语言,支持SQL进行数据查询可以降低用户使用大数据的门槛,让更多的用户能够使用大数据。
2020labs小助手
2022/10/24
2.7K0
几百行代码实现一个 JSON 解析器
之前在写 gscript 时我就在想有没有利用编译原理实现一个更实际工具?毕竟真写一个语言的难度不低,并且也很难真的应用起来。
crossoverJie
2022/10/27
4560
几百行代码实现一个 JSON 解析器
试用GO开发python编译器:实现词法解析
编译器由于涉及到编译原理,了解计算机科学的同学就能感触到,编译原理是较为抽象,无论从原理还是从实践上都是比较难把握的对象。在接触理论性较强,难度较大的问题时,最好的办法是从最简单的情况入手,先从感性上获得认知,为后面的理性认知打下基础,因此我们先从编译原理算法的基础入手,首先掌握词法解析。
望月从良
2021/12/31
5630
试用GO开发python编译器:实现词法解析
Calcite系列(六):执行流程-语法解析
目前广泛使用的语法解析框架主要包括ANTLR、JavaCC和Yacc等。在大数据领域中,很多计算引擎都是基于ANTLR进行语法解析,例如 Hive、Spark和Presto等都基于ANTLR进行处理。然而,Calcite使用JavaCC编译器进行语法解析。
Yiwenwu
2024/04/19
8980
Calcite系列(六):执行流程-语法解析
Go 译文之词法分析与解析 - Part One
一直对词法分析与解析的话题比较感兴趣,最近发现了好几篇相关的优秀文章,准备好好翻译和研究下。我的理解,词法分析与解析的应用还是比较广泛的,无论简单的配置文件、各种模板语言、还是我们每天在写编程语言都离不开它。
波罗学
2019/07/31
5180
【死磕Sharding-jdbc】---SQL解析-词法分析
sharding-jdbc对SQL解析的源码主要在下图所示parsing模块中,由下图可知SQL解析主要分为两部分:lexer和parser。lexer就是本文需要分析的词法分析:
用户1655470
2018/08/16
1.2K0
【死磕Sharding-jdbc】---SQL解析-词法分析
编译原理工程实践—02基于正则文法和有限自动机实现简易词法分析器
前面讲过,词法分析器的目标是从源码中识别出一个个"单词"Token。这个过程和人类"聆听"时边听边提取信息的过程类似,词法处理器也是边读取边处理字符,最终形成一个个连续的 Token。本章将通过一个简单的案例来体会词法处理器使用有限自动机实现 Token 的分割。
CS逍遥剑仙
2025/05/12
960
[WebKit] JavaScriptCore解析--基础篇(一)字节码的生成及抽象语法树的构建详情分析
看到HorkeyChen写的文章《[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现》,写的很好,深受启发。想补充一些Horkey没有写到的细节比如字节
程序员互动联盟
2018/03/12
1.5K0
[WebKit] JavaScriptCore解析--基础篇(一)字节码的生成及抽象语法树的构建详情分析
Go 译文之词法分析与解析 Part Two
本文是关于词法器实现的具体介绍,如果在阅读时遇到困难,建议参考源码阅读,文中的代码片段为了介绍思路。如何解析会在下一篇介绍。
波罗学
2019/07/31
5070
尝试用GO写python编译器:创建互动式命令号窗口REPL
上一节完成的词法解析器存在一些明显问题,例如在识别数字时,面对字符串“123abc”,它会识别为两者情况,分别为NUMBER:123,然后是IDENTIFIER:anc,实际上这样的字符串应该被认为是语法错误。另外我们还有一些”连号“操作符没有处理,例如“==, !=, — ,++”等。
望月从良
2021/12/31
4590
尝试用GO写python编译器:创建互动式命令号窗口REPL
动手实现一个JSON验证器(上)
分析 既然要验证JSON的有效性,那么必然需要清楚的知道JSON格式,这个在JSON官网已经给我们画出来了: 从官方的图上面可以看出,JSON的组成一共有五部分: object: 以左大括号({)开头
李海彬
2018/03/26
1.5K0
动手实现一个JSON验证器(上)
Mson,让JSON序列化更快
本文由秦喆 芝任 天洲 赵鹏四位作者共同完成。 问题 我们经常需要在主线程中读取一些配置文件或者缓存数据,最常用的结构化存储数据的方式就是将对象序列化为JSON字符串保存起来,这种方式特别简单而且可以
美团技术团队
2018/03/13
2.1K0
Mson,让JSON序列化更快
ChatGPT|AI自制编程语言-语法解析1
而本篇主要集中语义解析和AST树生成部分,还未实现求值(下一篇《语法解析1》实现求值功能)。
用户1904552
2025/02/27
1140
ChatGPT|AI自制编程语言-语法解析1
实现JavaScript语言解释器(一)
对于大多数前端开发者来说JavaScript可谓是我们最熟悉的编程语言了,它十分强大可是有些语言特性却十分难以理解,例如闭包和this绑定等概念往往会让初学者摸不着头脑。网上有很多诸如《你看完这篇还不懂this绑定就来砍我》之类的文章来为大家传道解惑。可是在我看来这些文章大多流于表面,你读了很多可能还是会被面试官问倒。那么如何才能彻彻底底理解这些语言特性,从而在面试的时候立于不败之地呢?在我看来要想真的理解一样东西,最好的途径就是实现这样东西,这也是西方程序员非常喜欢说的learning by implementing。例如,你想更好地理解React,那么最好的办法就是你自己动手实现一个React。因此为了更好地理解JavaScript的语言特性,我就自己动手实现了一个叫做Simple的JavaScript语言解释器,这个解释器十分简单,它基于TypeScript实现了JavaScript语法的子集,主要包括下面这些功能:
进击的大葱
2022/08/22
1.3K0
实现JavaScript语言解释器(一)
相关推荐
徒手撸一个JSON解析器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档