一个解析JSON的小栗子

232 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

JSON 的定义

JSON: JavaScript Object Notation(JavaScript 对象表示法)
JSON 是存储和交换文本信息的语法,类似 XML。
JSON 比 XML 更小、更快,更易解析。
JSON 易于人阅读和编写。

有哪些数据?

  • 数字(整数或浮点数)
  • 字符串(在双引号中)
  • 逻辑值(true 或 false)
  • 数组(在中括号中)
  • 对象(在大括号中)
  • null

递归解析

思路:
json 可以分为这么两种值, 一种是可以包含其他值的,一种是不可能包含其他值的。
对于可以包含其他值的, 即: {key: value, ...} 和 [value, value, ..., value] 不可用包含其他值的,有 数字,字符串,逻辑值,null
对于这种包含其他值的value可以是任意一种类型。
所以就形成了一个递归包含的形式 所以我们可以递归的去解析他们

存储

存储Json_type
bool boolean_;Boolean
double number_;Number
std::string string_;String
std::vector<Json> list_;Array
std::map<std::string, Json> dict_;Object
_NULL
class Json {
 public:
  enum Json_type {
    J_NULL_TYPE,
    J_BOOL_TYPE,
    J_NUMBER_TYPE,
    J_STRING_TYPE,
    J_LIST_TYPE,
    J_DICT_TYPE
  };
 private:
  Json_type type_;                   /* json type*/
  bool boolean_;                     /*bool */
  double number_;                    /int double float .../
  std::string string_;               /*string */
  std::vector list_;           /array/
  std::map<std::string, Json> dict_; /object/
};

解析

首先匹配每种类型开始的特征

 Json JsonParse::parse() {
  char token = nextToken_();
  switch (token) {
    case 'n':
      return parseNull();
    case 'f':
    case 't':
      return parseBool();
    case '-':
    case '1':case '2':case '3':
    case '4':case '5':case '6':
    case '7':case '8':case '9':
      return parseNumber();
    case '\"':
      return parseString();
    case '[':
      return parseList();
    case '{':
      return parseDict();
    default:
      throw std::logic_error("json grammar error");
  }
  return Json();
}

解析null

匹配4个字符 "null"

Json JsonParse::parseNull() {
  if (!data_.compare(idx_, 4, "null")) {
    idx_ += 4;
    return Json();
  } else {
    throw std::logic_error("json grammar error");
  }
}

解析boolean

匹配4个或5个字符 "true" | "false"

Json JsonParse::parseBool() {
  bool flag;
  if (data_[idx_] == 'f' && !data_.compare(idx_, 5, "false")) {
    idx_ += 5;
    flag = false;
    return Json(Json::J_BOOL_TYPE, &flag);
  } else if (data_[idx_] == 't' && !data_.compare(idx_, 4, "true")) {
    idx_ += 4;
    flag = true;
    return Json(std::move(flag));
  } else {
    throw std::logic_error("json grammar error");
  }
}

解析number

偷了一下懒,直接用strtod

Json JsonParse::parseNumber() {
  double res = 0;
  char* end;
  res = strtod(&data_[idx_], &end);
  if (&data_[idx_] == end) throw std::logic_error("json grammar error");
  idx_ += end - &data_[idx_];
  return Json(std::move(res));
}

解析string

在解析字符时,要注意转义字符的转换

std::string JsonParse::parseStr() {
  auto start = ++idx_;
  auto end = data_.find_first_of('"', start);
  if (end == data_.npos) throw std::logic_error("json grammar error");
  auto check = [&](int pos) -> bool {
    int pre = pos;
    while (pre && data_[pre] == '\\') pre--;
    return (pos - pre) % 2 == 0;
  };
  while (1) {
    if (check(end)) break;
    end = data_.find_first_of('"', end + 1);
    if (end == data_.npos) throw std::logic_error("json grammar error");
  }
  idx_ = end + 1;
  return data_.substr(start, end - start);
}

解析array

注意解析时, 空白字符要跳过,此外就是一个状态的转移过程。问题不大

Json JsonParse::parseList() {
  int flag = false;
  idx_++;
  std::vector<Json> list;
  while (idx_ < data_.size()) {
    char token = nextToken_();
    if (token == ',') {
      idx_++;
      continue;
    }
    if (token == ']') {
      idx_++;
      flag = true;
      break;
    }
    list.push_back(parse());
  }
  if (!flag) throw std::logic_error("json grammar error");
  return Json(std::move(list));
}

解析object

解析每一个value时, key就是一个字符串, value 是任意一种的对象

Json JsonParse::parseDict() {
  std::map<std::string, Json> dict;
  int flag = false;
  idx_++;
  while (idx_ < data_.size()) {
    char token = nextToken_();
    if (token == ',') {
      idx_++;
      continue;
    }
    if (token == '}') {
      idx_++;
      flag = true;
      break;
    }
    if (token != '\"') throw std::logic_error("json grammar error");
    std::string key = parseStr();
    token = nextToken_();
    if (token != ':') throw std::logic_error("json grammar error");
    idx_++;
    dict.insert({key, parse()});
  }
  if (!flag) throw std::logic_error("json grammar error");
  return Json(std::move(dict));
}

转成字符串

使用JSON时,另一个需求是把 类的对象 转换为一个 json 这个时候就需要把数据转为字符串了

转换状态转移

std::string Json::toString(int flag) {
  std::string str;
  switch (type_) {
    case J_NULL_TYPE:
      str.append(std::move(toNull_(flag)));
      break;
    case J_BOOL_TYPE:
      str.append(std::move(toBool_(flag)));
      break;
    case J_NUMBER_TYPE:
      str.append(std::move(toNumber_(flag)));
      break;
    case J_STRING_TYPE:
      str.append(std::move(toString_(flag)));
      break;
    case J_LIST_TYPE:
      str.append(std::move(toList_(flag)));
      break;
    case J_DICT_TYPE:
      str.append(std::move(toDict_(flag)));
      break;
  }
  return str;
}

转换null

std::string toNull_(int flag) { return "null"; }

转换boolean

std::string toBool_(int flag) {
  if (boolean_) {
    return "true";
  } else {
    return "false";
  }
}

转换string

std::string toString_(int flag) {
  std::string tmp;
  tmp.push_back('\"');
  tmp.append(std::move(string_));
  tmp.push_back('\"');
  return tmp;
}

转换number

std::string toNumber_(int flag) {
  if (floor(number_) != number_) return std::to_string(number_);
  return std::to_string(static_cast<int>(number_));
}

转换array

std::string toList_(int flag) {
  std::string tmp;
  tmp.push_back('[');
  for (auto v : list_) {
    tmp.append(std::move(v.toString(flag)));
    tmp.push_back(',');
  }
  tmp.pop_back();
  tmp.push_back(']');
  return tmp;
}

转换object

std::string toDict_(int flag) {
  std::string tmp;
  tmp.push_back('{');
  for (auto& v : dict_) {
    tmp.push_back('\"');
    tmp.append(std::move(v.first));
    tmp.append("\":");
    tmp.append(std::move(v.second.toString(flag)));
    tmp.push_back(',');
  }
  tmp.pop_back();
  tmp.push_back('}');
  return tmp;
}

利用模板来自动匹配对应的dump

template <typename T>
lins::Json json_dump(T rsh) {return rsh.dump();}

// 特化一下 基础类型的dump
lins::Json json_dump(int rsh) { return lins::Json(static_cast<double>(rsh)); }
lins::Json json_dump(bool rsh) { return lins::Json(rsh); }
lins::Json json_dump(std::string rsh) { return lins::Json(std::move(rsh)); }
template <typename T>
lins::Json json_dump(std::vector<T> rhs) {
  std::vector<lins::Json> json;
  for (auto v : rhs) json.push_back(json_dump(v));
  return lins::Json(std::move(json));
}
template <typename VALUE>
lins::Json json_dump(std::map<std::string, VALUE> rhs) {
  std::map<std::string, lins::Json> json;
  for (auto v : rhs) json.insert({v.first, json_dump(v.second)});
  return lins::Json(std::move(json));
}

利用宏来定义dump

#define BEGIN_DUMP_JSON \
  lins::Json dump() {   \
    std::map<std::string, lins::Json> res;

#define MAPPING(name, jsonName) res.insert({jsonName, lins::json_dump(name)});

#define END_DUMP_JSON                \
  return lins::Json(std::move(res)); \
  }

使用

使用时,按需所取, 定义自己的load函数。
需要导出的对象就用DUMP宏来映射出去

示例定义

struct person {
  std::string name;
  std::string detail;
  int age;
  void load(const lins::Json& json) {
    name = json["name"].STRING();
    detail = json["detail"].STRING();
    age = json["age"].NUMBER();
  }
  BEGIN_DUMP_JSON
  MAPPING(name, "name");
  MAPPING(detail, "detail");
  MAPPING(age, "age");
  END_DUMP_JSON
};
struct student {
  int stuNo;    // 学号
  int classNo;  // 班级
  person info;
  void load(const lins::Json& json) {
    stuNo = json["stuNo"].NUMBER();
    classNo = json["classNo"].NUMBER();
    info.load(json["info"]);
  }
  BEGIN_DUMP_JSON
  MAPPING(stuNo, "stuNo");
  MAPPING(classNo, "classNo");
  MAPPING(info, "info");
  END_DUMP_JSON
};

struct theacher {
  int studentNo;
  person info;
  void load(const lins::Json& json) {
    studentNo = json["studentNo"].NUMBER();
    info.load(json["info"]);
  }
  BEGIN_DUMP_JSON
  MAPPING(studentNo, "studentNo");
  MAPPING(info, "info");
  END_DUMP_JSON
};

struct classroom {
  std::vector<student> stu;
  std::vector<theacher> tch;
  std::map<string, int> mp;
  void load(const lins::Json& json) {
    for (auto& v : json["stu"].LIST()) {
      auto t = student();
      t.load(v);
      stu.push_back(t);
    }
    for (auto& v : json["theacher"].LIST()) {
      auto t = theacher();
      t.load(v);
      tch.push_back(t);
    }
  }
  BEGIN_DUMP_JSON
  MAPPING(stu, "stu");
  MAPPING(tch, "tch");
  MAPPING(mp, "mp");
  END_DUMP_JSON
};

使用

classroom room = \ 
  {
      .stu{
          student{1001, 5, {"张三", "一个学生", 15}},
          student{1002, 5, {"李四", "一个学生", 16}},
          student{1003, 5, {"王五", "学生一个", 19}},
          student{1004, 5, {"坤坤", "唱跳rap篮球", 18}},
          student{1005, 5, {"老六", "一个学生", 19}},
          student{1006, 5, {"青霞", "一个学生", 19}},
      },
      .tch{
          theacher{10086, {"李老师", "语文", 45}},
          theacher{10087, {"张老师", "数学", 35}},
          theacher{10081, {"结衣老师", "老师", 25}},
      },
  };
auto res = room.dump();
auto str = res.toString(true);
cout << lins::JsonParse(str).parse().toString() << endl;

完整代码

github.com/Amjieker/li… github.com/Amjieker/li…

测试代码

github.com/Amjieker/li…