本文的目的,不是针对现有的可用于生产环境的JSON、XML解析器源码进行剖析,而是介绍文本扫描的基础方法next(char),并以此为核心武器,根据目标语言的词法和语法特点,一步步地组织出条例清晰、易维护的解析器代码。希望这会是一篇实践性强,让您有所收获的文章。

另外,这里需要提前说明的是,本文所实现的解析器仅作为coding练习使用。一些目标语言的规范中提到的语法,可能无法正常解析。另外,本文所实现的解析器也缺少大量的实例进行测试。请不要用于生产用途。

前言

作为一个非科班前端程序员,我最近特别痴迷于自学《编译原理》这门课。原因在于,自己大学时代的专业是语言学,其中的理论有颇多相似之处;再加上前端工作中,模版编译成render function,webpack通过loader加载文件等都方面涉及到了编译。我也希望自己能多了解一些编译知识,说不定能在日后的前端工作中能够发挥奇效。

看了一些youtube上的公开课资源,啃了一点龙书这样的编译原理经典作品后,我感觉自己只了解了一堆关于词法法解析、语法解析的理论总结,很难从中获得“学会了”、“会用了”这样的成就感。于是在稍稍有了一点知识基础后,我开始寻找github上关于解析器的源码。

JSON的解析

这里想给大家推荐的是JSON之父,Douglas Crockford的repo: JSON-js中的这个源代码,它也是本文的灵感源泉:

JSON-js/json_parse.js at master · douglascrockford/JSON-js

据代码注释,这个文件实现了JSON.parse方法,使用的解析手段是recursive decending(递归下降分析)。

在同一个repo里还有一个json_parse_state.js文件,也是JSON.parse方法的实现,使用的解析手段是state machine(状态机)。

其实我个人认为上文链接中的源代码使用的解析手段也是state machine,因为recursive decending应该是语法分析使用的方法来着= =。

但从代码的清晰度上来看,json_parse.js要好不少,所以更推荐阅读。

快速地过一遍源码,我们可以发现一个核心函数:

var next = function (c) {// If a c parameter is provided, verify that it matches the current character.if (c && c !== ch) {error("Expected '" + c + "' instead of '" + ch + "'");}// Get the next character. When there are no more characters,
// return the empty string.ch = text.charAt(at);at += 1;return ch;
};
复制代码

这个方法相当于一个字符扫描器,其中使用的全局变量at是当前扫描光标所处位置的索引,ch是当前扫描光标所处位置的字符。调用next方法时,如果传入了参数c(也是一个字符),则会比较此字符与当前扫描器所在的字符,如果不相同就会报错,并且扫描光标不会向前移动;如果未传参数,扫描光标的位置和所指的字符都会向前更新一个位置。

这份代码中的其他函数,充斥着对next的调用,让我们来看几个例子感受一下next的用法。

var word = function () {// true, false, or null.switch (ch) {case "t":next("t");next("r");next("u");next("e");return true;case "f":next("f");next("a");next("l");next("s");next("e");return false;case "n":next("n");next("u");next("l");next("l");return null;}error("Unexpected '" + ch + "'");
};
复制代码

word函数用来处理JSON中的三个常量token,即true, falsenull。整个函数根据首字母,分别接收t->r->u->e,f->a->l->s->e,n->u->l->l这样的字符输入。如果其中出现了其他顺序的字符输入,都会抛出Error。word方法还会在匹配token的同时,返回所匹配到的token的值。3个return语句所出现的位置,表示word函数已经接受了这段字符输入,并成功解析出了一个值。

再来看另一个不传参调用next()的例子:

var white = function () {// Skip whitespace.while (ch && ch <= " ") {next();}
};
复制代码

white函数的作用仅在于跳过空白,只要当前字符是属于空白的,就不停地调用next()作无条件后跳。

源码中还有number和string函数,其用途和上面的word, white一样,只不过逻辑更为复杂,可以解析出不定长度、不定字符组合的数字和字符串。

一步一步地写出这些“零件”的解析函数后,我们就可以进一步写出一些复合结构的解析函数了,也就是源码中的array和object函数。

最后,源码中实现了可以解析任意一个JSON元素的value函数。从语法的角度讲,这里所定义的value,可以是任何一个string, number, array或object,至此,我们就完成了解析所有JSON元素需要的函数。

以上就是解析的核心代码了,个人认为十分地易于理解并且有明确的分层,易于维护以及以后增加功能。我也在这里用同样的next函数的手法,尝试重写了这个JSON解析器源代码。作为练习,我没有实现escape或revive等功能,但把各个解析函数拆分得更加精细(例如为每个单字符token都写了解析函数,将array拆解为[ + elements + ]等),使得代码更易读。地址是:

18 JSON parser

XML的解析

有了上面的JSON解析器实现的“手感”,我又尝试着用同样的next函数手法,部分地实现了XML的解析。和JSON相比,个人在实现过程中发现的坑点主要在于:

  • JSON对象基本上就是JavaScript中Object对象的字面化表示,所以每次解析出来一小段之后,直接以JavaScript数列或对象的形式保存即可。XML节点需要为其定义类似下面的数据结构,所以代码的复杂度略有增加:
Node {tagName //节点标签名attrs //节点上的属性,为key/value的数组children //节点的子节点,为Node的数组
}
复制代码
  • XML对象必须作语法分析,也就是close tag有没有匹配的问题。诸如<a><b></a></b>这样的XML需要提示解析错误。不过实现这个也很简单,使用一个nodeStack栈,在opentag时推入节点;在closetag时检查当前节点是否和栈尾的tag相匹配,匹配则推出末尾的节点;在comment节点或text节点时不作处理即可。
  • comment节点的结束判断。comment节点的格式是<!--content-->,因此在解析content部分时,每输入一个字符,需要作3个字符的提前判断。即,如果当前所读到的字符的接下来三个字符分别是-->时,停止解析。

我所实现的XML解析器的代码如下(没有实现self-closing tag的解析功能,例如<br>, <input>等。所有tag必须成对出现):

20 XML parser

转载于:https://juejin.im/post/5a46e174518825698e726486

自己实现JSON、XML的解析 没那么难相关推荐

  1. 零基础iOS之Json及XML数据解析2

    零基础iOS之Json及XML数据解析http://www.cnblogs.com/dingjianjaja/articles/4798604.html 转载于:https://www.cnblogs ...

  2. delphi读取xml中的内容property name传递参数_python3 Json和XML数据解析

    一.Json数据解析 Python3 中可以使用 json 模块来对 JSON 数据进行编解码,它包含了两个函数: json.dumps(): 对数据进行编码. json.loads(): 对数据进行 ...

  3. Java XML转换为JSON XML解析 转换为JSON Java 实现JSON转换为XML json转xml

    Java XML转换为JSON XML解析 转换为JSON Java 实现JSON转换为XML json转xml 一.转换代码 1.XML字符串转换为JSON /*** description: XM ...

  4. Python Xml dom解析 输出json操作

    将region.xml文件解析为  列表  json格式 以下为xml文件部分内容: 1.DOMTree = xml.dom.minidom.parse('region.xml')       (获取 ...

  5. 计算机程序的思维逻辑 (63) - 实用序列化: JSON/XML/MessagePack

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>(马俊昌著),由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买:京东自营链接 ...

  6. Python3将xml文件解析为Python对象

    一.说明 从最开始写javascript开始,我就很烦感使用getElementById()等函数来获取节点的方法,获取了一个节点要访问其子孙节点要么child半天要么就再来一个getElementB ...

  7. SpringMVC json/xml自动转换

    为什么80%的码农都做不了架构师?>>>    前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www. ...

  8. iOS - XML 数据解析

    前言 @interface NSXMLParser : NSObjectpublic class NSXMLParser : NSObject 1.XML 数据 XML(Extensible Mark ...

  9. Json字符串解析原理、超大json对象的解析

    概述 附上完整的代码: https://files.cnblogs.com/files/xcr1234/json.rar 一个类实现json解析核心代码(ObjectParser),其他的类都是工具类 ...

最新文章

  1. 人脸识别引爆下一代生物支付四军之战
  2. 编译Caffe-Win错误集锦
  3. MyBatis 配置文件 用户密码加密存储
  4. JVM面试必问:G1垃圾回收器
  5. Docker-CE 入门
  6. Eclipse中JSP生成的class文件去了哪里?
  7. linux网络测试(必会)
  8. 安卓开发 在oncreate显示对话框 hide 之后 点不动_微信小程序云开发教程微信小程序的API入门常用API...
  9. 数字信号处理基础----拉普拉斯变换与Z变换
  10. 实战Java商品库存管理系统
  11. 儿童电子产品的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  12. EasyUI Combobox 的 onChange,onSelect,onClick 事件
  13. Unity3d版数字地球、谷歌地球(google earth)
  14. css所有属性大合集,包含中文标题
  15. 2023-2028年中国黄酒行业市场预测与投资规划分析报告
  16. 爬虫漫游指南:HTTP/2 网站爬取
  17. 华为k662c的虚拟服务器,华为k662c光猫怎么样? 华为K662c拆机技巧
  18. 加班两年只赚2千块:低姿态的人,挣不了大钱
  19. 安卓开发入门--第一个手机APP
  20. python 复制dict_Python高级数据类型之字典、集合【明哥陪你学Python-六】

热门文章

  1. 程序员必备的 10 大 GitHub 仓库
  2. myeclipse10中表单中的JS函数无法写return,onsubmit=return check();处出错
  3. java 两个窗口 贴_求助Java窗口菜单如何实现复制粘贴剪切等功能(内附源代码)...
  4. ajax局部刷新_web前端入门到实战:实现html页面自动刷新
  5. 全局对象_C++全局变量初始化
  6. keil uvision2 c语言调试,keil uvision2
  7. php 加密cer_php 生成RSA非对称加密用的证书-cer-pfx文件
  8. python求取列表中的质数
  9. (5) 百度2011研发工程师笔试卷
  10. html插音乐怎么设置样式,是否可以设置html5音频标签的样式?