简介
本文设计并实现了一个简化版的json解析器。可解析json中的对象
、数组
和字符串
。同时解析器还可把对象编码成json的格式。实现采用C++语言。
需要前置知识:
- 编译原理
- C++程序设计
文法
根据旧版json的文法规则,可反推出如下上下文无关文法,并使用EBNF表示:
其中 \uxxxx 表示除了 \ 和 " 以外的字符
<json> -> <object> | <array> | <string>
<object> -> '{' {<string> : <json> , } <string> : <json> '}'
-> '{' '}'
<array> -> '[' { <json> , } <json> ']'
-> '[' ']'
<string> -> "{<char>}"
<char> -> `\uxxxx`
-> \ (\ | ")
为了能够变成递归下降算法
可处理的文法,即LL(1)文法。重写后的文法如下:
<json> -> <object> | <array> | <string>
<object> -> '{' [ <string> : <json> { , <string> : <json> } ] '}'
<array> -> '[' [ <json> { , <json> } ] ']'
<string> -> "{<char>}"
<char> -> `\uxxxx`
-> \ (\ | ")
词法分析
本文把<string>
的识别作为词法分析的内容。因此,词法分析的任务就是从输入字符序列中识别以下符号:
符号 | 对应的识别内容 | 正规式 |
---|---|---|
对象左边界 | { | { |
对象右边界 | } | } |
数组左边界 | [ | [ |
数组右边界 | ] | ] |
字符串表达式 | <string> | “(\\ | \” |\uxxxx)*" |
逗号 | , | , |
冒号 | : | : |
由于字符串表达式比较复杂,而其它的符号(又称单词)很容易可以画出其最小的DFA,因此下面重点只介绍字符串表达式的DFA推导过程。
首先根据正规式,画出其NFA,如下:
根据NFA转换DFA的算法,画出如下用于推导的表格:
其中S表示开始状态,Z表示结束状态。
状态 \ 字符 | " | \ | \uxxxx |
---|---|---|---|
{S} | {A} | ∅ | ∅ |
{A} | {Z} | {B, C} | {A} |
{Z} | ∅ | ∅ | ∅ |
{B, C} | {A} | {A} | ∅ |
将以上出现的状态从上至下重新命名为:S、S1、Z、S2,绘制出DFA如下:
发现并没有多余的状态或等价的状态,因此DFA已经最小。根据此DFA,可编写出相应的词法分析程序。见实现部分。
语法分析
语法分析使用递归下降算法
。每一个非终结符都将由一个函数来处理。语法分析部分需要识别以下三个非终结符:
非终结符 | 处理函数 | 备注 |
---|---|---|
<json> | inferElement() | 自顶向下推导json |
<object> | inferObject() | 自顶向下推导对象表达式 |
<array> | inferArray() | 自顶向下推导数组表达式 |
由于<string>在词法分析阶段已经识别,因此在此阶段视为终结符
。
语义分析及目标代码生成
语义分析和语法分析同时进行。每当一个非终结符被识别完成,则生成一个对应的C++对象(可看作是目标代码)。当最顶层的非终结符识别完成,则翻译工作完成。
中间代码及优化略去!
实现
以下是是完整的实现代码。您可直接拷贝到您的项目中作为一个单独的文件,并以头文件的方式引用。
/**
* JSON 解析器-简化版
* 本解析器实现了以下两个功能:
* 1.把字符串转成编程语言可处理的对象
* 2.构造对象从而生成json字符串
* 但因为时间关系,懒得做得太复杂~~,因此存在如下局限性:
* 1. json语法经过了简化,只支持string,array和object,不支持null、number、boolean,因此不适用于生产环境
* 2. 不支持中文字符的处理
* */
#pragma once
#include <vector>
#include <map>
#include <string>
#include <memory>
#include <sstream>
using namespace std;
namespace json{
/**
* 通用基类
*/
class Element{
public:
virtual shared_ptr<string> toString() = 0;
};
/**
* 对象
*/
class Object : public Element {
private:
map<string, shared_ptr<Element>> attributes;
public:
/** 根据key获取值 */
shared_ptr<Element> get(string key){
auto iter = this->attributes.find(key);
shared_ptr<Element> element = nullptr;
if(iter != this->attributes.end()){
element = iter->second;
}
return element;
}
/** 设置元素 */
void set(string key, shared_ptr<Element> value){
this->attributes[key] = value; // 暂时不支持null值,所以不用判定
}
/** 转成字符串 */
virtual shared_ptr<string> toString(){
stringstream ss;
ss << "{";
for(auto i = this->attributes.begin(); i != this->attributes.end(); ){
ss << "\"";
ss << i->first;
ss << "\"";
ss << ":";
ss << *(i->second->toString());
i ++;
if(i != this->attributes.end()){
ss << ",";
}
}
ss << "}";
return make_shared<string>(ss.str());
}
};
/**
* 数组
*/
class Array : public Element{
private:
vector<shared_ptr<Element>> elements;
public:
/** 数组元素个数 */
int size(){
return this->elements.size();
}
/** 添加元素到尾 */
void add(shared_ptr<Element> element){
this->elements.push_back(element);
}
/** 根据索引获取元素 */
shared_ptr<Element> get(int index){
shared_ptr<Element> element = nullptr;
if(index >= 0 && index < size()){
element = this->elements[index];
}
return element;
}
/** 转成字符串 */
virtual shared_ptr<string> toString(){
stringstream ss;
ss << "[";
for(int i = 0; i < size(); i++){
ss << *(this->elements[i]->toString());
if (i != size() - 1){
ss << ",";
}
}
ss << "]";
return make_shared<string>(ss.str());
}
};
/**
* 字符串
*/
class String : public Element {
private:
shared_ptr<string> str = nullptr;
public:
/**
* 获取内置字符串
*/
shared_ptr<string> get(){
return this->str;
}
/**
* 设置内置字符串
*/
void set(shared_ptr<string> str){
this->str = str;
}
/**
* 转成字符串
*/
virtual shared_ptr<string> toString(){
stringstream ss;
ss << "\"";
for(int i = 0; i < this->str->size(); i++){
char ch = (*(this->str))[i];
if(ch == '\"' || ch == '\\'){
ss << '\\';
}
ss << ch;
}
ss << "\"";
return make_shared<string>(ss.str());
}
};
/**
* Facade 类
* 用于把字符串解析成对象
*/
class JSON{
private:
static const string LEXICAL_ERROR;
static const string GRAMMA_ERROR;
public:
/**
* 词法分析的目标。
* */
enum class Symbol {
OBJECT_L_BOUND, // 对象左边界
OBJECT_R_BOUND, // 对象右边界
ARRAY_L_BOUND, // 数组左边界
ARRAY_R_BOUND, // 数组右边界
STRING, // 字符串表达式
COMMA, // 逗号
COLON, // 冒号
EOS // 结束标志,即#
};
/**
* 符号对象
* */
struct Word{
Symbol symbol;
string value;
Word(Symbol symbol)
: Word(symbol, "")
{}
Word(Symbol symbol, string value)
: symbol(symbol)
, value(value)
{}
};
/**
* 词法分析器
* 把输入的字符序列翻译成为单词串
* @param str 源语言
* @return 单词列表。(逆序存储)
*/
static vector<Word> lexical_analyze(string str){
vector<Word> words;
// 总字符个数
int count = str.size();
// 当前字符
int sym = 0;
// 开始循环查找单词
while (sym < count)
{
if(str[sym] == ' ' || str[sym] == '\r' || str[sym] == '\n' || str[sym] == '\t' || str[sym] == '\f'){
sym += 1;
}else if(str[sym] == '{'){
words.push_back(Word(Symbol::OBJECT_L_BOUND));
sym += 1;
}else if(str[sym] == '}'){
words.push_back(Word(Symbol::OBJECT_R_BOUND));
sym += 1;
}else if(str[sym] == '['){
words.push_back(Word(Symbol::ARRAY_L_BOUND));
sym += 1;
}else if(str[sym] == ']'){
words.push_back(Word(Symbol::ARRAY_R_BOUND));
sym += 1;
}else if(str[sym] == ','){
words.push_back(Word(Symbol::COMMA));
sym += 1;
}else if(str[sym] == ':'){
words.push_back(Word(Symbol::COLON));
sym += 1;
}else if(str[sym] == '\"'){
sym += 1;
if(sym >= count) throw LEXICAL_ERROR;
stringstream ss;
while (true)
{
if(str[sym] == '\\'){
sym += 1;
if(sym >= count) throw LEXICAL_ERROR;
if(str[sym] == '\\'){
ss << '\\';
}else if(str[sym] == '\"'){
ss << '\"';
}else {
throw LEXICAL_ERROR;
}
sym += 1;
if(sym >= count) throw LEXICAL_ERROR;
}else if(str[sym] == '\"'){
words.push_back(Word(Symbol::STRING, ss.str()));
sym += 1;
break;
}else{
ss << str[sym];
sym += 1;
if(sym >= count) throw LEXICAL_ERROR;
}
}
}else {
throw LEXICAL_ERROR;
}
}
return words;
}
private:
/**
* 推导object
*/
shared_ptr<Object> inferObject(const vector<Word> & words, int & current){
shared_ptr<Object> object = make_shared<Object>();
if(words[current].symbol != Symbol::OBJECT_L_BOUND){
throw GRAMMA_ERROR;
}
current += 1;
const Word& w = words[current];
if(w.symbol == Symbol::OBJECT_R_BOUND){
current += 1;
return object;
}else{
if(w.symbol != Symbol::STRING){
throw GRAMMA_ERROR;
}
current += 1;
if(words[current].symbol != Symbol::COLON){
throw GRAMMA_ERROR;
}
current += 1;
shared_ptr<Element> element = inferElement(words, current);
object->set(w.value, element);
while (words[current].symbol == Symbol::COMMA)
{
current += 1;
const Word& w2 = words[current];
if(w2.symbol != Symbol::STRING){
throw GRAMMA_ERROR;
}
current += 1;
if(words[current].symbol != Symbol::COLON){
throw GRAMMA_ERROR;
}
current += 1;
shared_ptr<Element> element2 = inferElement(words, current);
object->set(w2.value, element2);
}
if(words[current].symbol != Symbol::OBJECT_R_BOUND){
throw GRAMMA_ERROR;
}
current += 1;
return object;
}
}
/**
* 推导array
*/
shared_ptr<Array> inferArray(const vector<Word> & words, int & current){
shared_ptr<Array> array = make_shared<Array>();
if(words[current].symbol != Symbol::ARRAY_L_BOUND){
throw GRAMMA_ERROR;
}
current += 1;
const Word& word = words[current];
if(word.symbol == Symbol::ARRAY_R_BOUND){
current += 1;
return array;
}else{
shared_ptr<Element> element = inferElement(words, current);
array->add(element);
while(words[current].symbol == Symbol::COMMA){
current += 1;
shared_ptr<Element> element2 = inferElement(words, current);
array->add(element2);
}
if(words[current].symbol != Symbol::ARRAY_R_BOUND){
throw GRAMMA_ERROR;
}
current += 1;
return array;
}
}
/**
* 推导json
*/
shared_ptr<Element> inferElement(const vector<Word> & words, int & current){
const Word& word = words[current];
if(word.symbol == Symbol::STRING){
shared_ptr<String> str = make_shared<String>();
str->set(make_shared<string>(word.value));
current += 1;
return str;
}else if(word.symbol == Symbol::OBJECT_L_BOUND){
return inferObject(words, current);
}else if(word.symbol == Symbol::ARRAY_L_BOUND){
return inferArray(words, current);
}else {
throw GRAMMA_ERROR;
}
}
/**
* 语法分析器
* 根据源语言单词序列转换成对象
* @param words 源语言的单词序列
* @return 分析完成翻译的json对象
*/
shared_ptr<Element> gramma_analyze(const vector<Word> & words){
int current = 0;
return inferElement(words, current);
}
public:
/**
* 解析器
* 把源语言字符串依次经过词法分析、语法分析、语义分析
* 最终生成目标语言,即c++对象
* */
shared_ptr<Element> parse(string str){
vector<Word> words = lexical_analyze(str);
words.push_back(Word(Symbol::EOS));
shared_ptr<Element> element = gramma_analyze(words);
return element;
}
};
const string JSON::LEXICAL_ERROR = "词法错误";
const string JSON::GRAMMA_ERROR = "语法错误";
};
使用示例
解析json字符串
本示例从文件中读取json字符串,并进行解析。文件内容如下:
{
"code": "00000",
"message": "operate successfully! test slash: \\ , test quota: \"",
"data": [
{"id": "1", "name": "zhangsan", "age": "18", "authorities": ["admin", "user"]},
{"id": "2", "name": "lisi", "age": "18", "authorities": ["admin"]},
{"id": "3", "name": "wangwu", "age": "18", "authorities": ["user"]}
]
}
下面是是使用解析器json::JSON进行解析的代码
#include <iostream>
#include <fstream>
#include <cassert>
#include "json.hpp" // 引用库文件
using namespace std;
void example_decode(){
// 读取文件内容
ifstream is("json.json");
if(is.fail()){
cout << "read file failed" << endl;
return;
}
ostringstream stringBuffer;
string line;
while(getline(is, line)){
stringBuffer << line << endl;
}
is.close();
cout << "读取文件结果:" << endl;
cout << stringBuffer.str() << endl;
// json解析
json::JSON parser;
shared_ptr<json::Element> element = parser.parse(stringBuffer.str());
cout << "解析结果:"<< *(element->toString()) << endl;
cout << "读取数据: " << endl;
// 数据读取
shared_ptr<json::Object> object = dynamic_pointer_cast<json::Object>(element);
shared_ptr<json::String> code = dynamic_pointer_cast<json::String>(object->get("code"));
shared_ptr<json::String> message = dynamic_pointer_cast<json::String>(object->get("message"));
shared_ptr<json::Array> data = dynamic_pointer_cast<json::Array>(object->get("data"));
cout << "code = " << *(code->get()) << endl;
cout << "message = " << *(message->get()) << endl;
for(int i = 0; i < data->size(); i++){
shared_ptr<json::Object> obj = dynamic_pointer_cast<json::Object>(data->get(i));
shared_ptr<json::String> id = dynamic_pointer_cast<json::String>(obj->get("id"));
shared_ptr<json::String> name = dynamic_pointer_cast<json::String>(obj->get("name"));
shared_ptr<json::String> age = dynamic_pointer_cast<json::String>(obj->get("age"));
shared_ptr<json::Array> authorities = dynamic_pointer_cast<json::Array>(obj->get("authorities"));
cout << "id = " << *(id->get()) << ",";
cout << "name = " << *(name->get()) << ",";
cout << "age = " << *(age->get()) << ",";
cout << "authorities = ";
for(int j = 0; j < authorities->size(); j++){
shared_ptr<json::String> authority = dynamic_pointer_cast<json::String>(authorities->get(j));
cout << *(authority->get()) << " ";
}
cout << endl;
}
}
int main(){
example_decode();
return 0;
}
运行结果如下。可以发现数据能够正常从json字符串的解析出来。包括转义字符。
编码json字符串
#include <iostream>
#include <fstream>
#include <cassert>
#include "json.hpp" // 引用库文件
using namespace std;
void example_encode(){
shared_ptr<json::Object> object = make_shared<json::Object>();
shared_ptr<json::Array> settings = make_shared<json::Array>();
object->set("settings", settings);
for(int i = 0; i < 5; i++){
shared_ptr<json::Object> setting = make_shared<json::Object>();
shared_ptr<json::String> settingId = make_shared<json::String>();
shared_ptr<json::String> settingInfo = make_shared<json::String>();
settingId->set(make_shared<string>("S00X"));
settingInfo->set(make_shared<string>("this is setting info..."));
setting->set("id", settingId);
setting->set("info", settingInfo);
settings->add(setting);
}
cout << *(object->toString()) << endl;
}
int main(){
example_encode();
return 0;
}
运行结果如下。
总结
本文实现了一个简化版的json解析器,同时支持编码的功能。显然,功能是够完善的。如有意向合作完成完整版的解析器、替换更优的解析算法、实现json5解析等,可通过邮箱 cloudea@163.com 与我联系。