JsonCpp源码分析——Reader

1、与Writer模块功能相反,可以将Reader理解成一个反序列化的工具,Writer的作用主要是将Value对象转成string或者流式的结构,Reader的作用主要是将流式的结构转成Value类型的对象。Reader类的主要职责有3个,解析 JSON 字符串:将 JSON 格式的字符串读取并解析成相应的 C++ 数据结构。处理不同的数据类型,支持解析 JSON 对象、数组、字符串、数字、布尔值和 null。处理错误,在解析过程中,如果遇到格式错误或不符合 JSON 标准的情况,Reader 类能够捕获这些错误并提供相关的错误信息。示例代码如下:

#include <json/json.h>
#include <iostream>
#include <string>

int main() {
    std::string jsonString = R"({"name": "Alice", "age": 30, "is_student": false})";
    Json::CharReaderBuilder readerBuilder;
    Json::Value root;
    std::istringstream jsonStream(jsonString);
    std::string errs;

    // 解析 JSON 字符串
    if (Json::parseFromStream(readerBuilder, jsonStream, &root, &errs)) {
        std::cout << "Name: " << root["name"].asString() << std::endl;
        std::cout << "Age: " << root["age"].asInt() << std::endl;
        std::cout << "Is Student: " << root["is_student"].asBool() << std::endl;
    } else {
        std::cerr << "Error parsing JSON: " << errs << std::endl;
    }

    return 0;
}

下面这个接口是实际开发中经常会用到的接口,当然我可以也可以向StreamWriterBuilder那样设置解析的属性,这个不难,不再进行介绍,后面会详细介绍下实现解析功能的精髓Token的设计。
bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
String* errs);
2、Token是Reader模块中的精髓,它是jsoncpp 库中用于表示 JSON 数据流中各个组成部分的类。在 JSON 数据解析的过程中,Token 代表了 JSON 字符串中的基本元素,如对象的开始、数组的结束、字符串、数字等。通过对这些标记的处理,jsoncpp 能够逐步解析 JSON 数据并将其转换为 C++ 数据结构。Token的定义如下:

  enum TokenType {
    tokenEndOfStream = 0,     //数据流的结束
    tokenObjectBegin,         //对象的开始 {
    tokenObjectEnd,           //对象的结束 }
    tokenArrayBegin,          //数组的开始 [
    tokenArrayEnd,            //数组的结束 ]
    tokenString,              // string
    tokenNumber,
    tokenTrue,
    tokenFalse,
    tokenNull,
    tokenArraySeparator,      //数组元素分隔符 ,
    tokenMemberSeparator,     // 对象成员分隔符 ,
    tokenComment,             // 注释
    tokenError                // 解析出错
  };

  class Token {
  public:
    TokenType type_;
    Location start_;
    Location end_;
    // start_ 表示标记的开始位置,end_ 表示标记的结束位置,识别出TokenType会根据这两个指针去解析对应的数据
  };

我觉着在解析Object时可以帮助理解Token的作用

bool Reader::readObject(Token& token) {
  Token tokenName;
  String name;
  Value init(objectValue);  // 创建一个对象类型的初始值
  currentValue().swapPayload(init);  // 将当前值设置为初始化的对象值
  currentValue().setOffsetStart(token.start_ - begin_);  // 设置对象的起始位置
  while (readToken(tokenName)) {  // 读取下一个标记
    bool initialTokenOk = true;
    // 跳过可能存在的注释
    while (tokenName.type_ == tokenComment && initialTokenOk)
      initialTokenOk = readToken(tokenName);
    if (!initialTokenOk)
      break;  // 如果读取注释失败,则退出循环

    // 如果遇到对象结束标记且没有名称,则表示对象为空
    if (tokenName.type_ == tokenObjectEnd && name.empty())
      return true;

    name.clear();  // 清除之前的名称
    // 处理对象的成员名称
    if (tokenName.type_ == tokenString) {
      if (!decodeString(tokenName, name))  // 解析字符串名称
        return recoverFromError(tokenObjectEnd);  // 解析错误,恢复错误处理
    } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
      Value numberName;
      if (!decodeNumber(tokenName, numberName))  // 解析数字名称
        return recoverFromError(tokenObjectEnd);  // 解析错误,恢复错误处理
      name = numberName.asString();  // 将数字转换为字符串作为名称
    } else {
      break;  // 如果名称不是字符串或数字,则退出循环
    }

    Token colon;
    // 读取冒号,检查是否存在冒号分隔符
    if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
      return addErrorAndRecover("Missing ':' after object member name", colon, tokenObjectEnd);  // 如果缺少冒号,记录错误并恢复
    }

    Value& value = currentValue()[name];  // 获取当前值的对应成员
    nodes_.push(&value);  // 将成员值推入栈中
    bool ok = readValue();  // 读取成员的值
    nodes_.pop();  // 从栈中弹出成员值
    if (!ok)  // 如果读取值失败,错误已设置
      return recoverFromError(tokenObjectEnd);

    Token comma;
    // 读取逗号或对象结束标记,检查是否存在有效的分隔符
    if (!readToken(comma) ||
        (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
         comma.type_ != tokenComment)) {
      return addErrorAndRecover("Missing ',' or '}' in object declaration", comma, tokenObjectEnd);  // 如果缺少逗号或结束标记,记录错误并恢复
    }

    bool finalizeTokenOk = true;
    // 跳过可能存在的注释
    while (comma.type_ == tokenComment && finalizeTokenOk)
      finalizeTokenOk = readToken(comma);
    if (comma.type_ == tokenObjectEnd)
      return true;  // 如果遇到对象结束标记,则成功解析对象并返回
  }

  return addErrorAndRecover("Missing '}' or object member name", tokenName, tokenObjectEnd);  // 如果缺少对象结束标记或成员名称,记录错误并恢复
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值