jsoncpp库常用源码解析及使用介绍(三)

        前面两节介绍了jsoncpp库中使用最频繁的Value类,介绍了它的多种构造函数、重载符号、判断函数、转换函数以及其他功能性函数,同时,还举例对这些函数进行了使用说明。在jsoncpp库中,还有两个很重要的类Reader和Writer,本篇对Reader类的源码及使用进行介绍。
        Json::Reader类顾名思义主要用于读取,它可以将字符串转换成Json::Value对象,或者从一个json文件中读取内容然后转成Json::Value对象。首先看它的构造函数:
 

//Reader类的构造函数
Reader();
Reader(const Features& features);

//Reader类构造函数的实现
Reader::Reader()
    : errors_(), document_(), commentsBefore_(), features_(Features::all()) {}

Reader::Reader(const Features& features)
    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
}

//在这个实现中,初始化了一些对象,其中:
//errors_定义如下,它主要就是用于记录读取时遇到的错误,错误代码如下,大致根据英文能看出来错误类型:
Errors errors_;

enum TokenType {
    tokenEndOfStream = 0,
    tokenObjectBegin,
    tokenObjectEnd,
    tokenArrayBegin,
    tokenArrayEnd,
    tokenString,
    tokenNumber,
    tokenTrue,
    tokenFalse,
    tokenNull,
    tokenArraySeparator,
    tokenMemberSeparator,
    tokenComment,
    tokenError
  };

  class Token {
  public:
    TokenType type_;
    Location start_;
    Location end_;
  };

  class ErrorInfo {
  public:
    Token token_;
    String message_;
    Location extra_;
  };

  typedef std::deque<ErrorInfo> Errors;

//存储读取的字符串
String document_;

//存储JSON的注释内容
String commentsBefore_;

//Feature类:
Features features_;
//它主要是用于设定读取时的一些特性,在Feature类的定义中,可以看到一些默认的特性,实现如下:
/// \c true if comments are allowed. Default: \c true.
bool allowComments_{true};

/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_{false};

/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_{false};

/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_{false};

Features::Features() = default;
Features Features::all() { return {}; }

/*Feature类的构造函数如上所示,可以看到,在Reader类的两个构造函数中,Reader()就是默认全部特性依照默认的设置,Reader(const Feature& feature)就是按用户设置的特性进行构造,这样在读取时会按照设置的特性读取。*/

        在Reader类的两个构造函数中,Reader()就是默认全部特性依照默认的设置,Reader(const Feature& feature)就是按用户设置的特性进行构造,这样在读取时会按照设置的特性读取。默认情况下,一般都是使用Reader()这个构造函数的。接下来就是最重要的parse函数,它用于从一个json文件中读取内容并转换成Value对象,它的构造有如下三种形式:

bool parse(const std::string& document, Value& root, bool collectComments = true);
bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true);
bool parse(IStream& is, Value& root, bool collectComments = true);

        这三个parse函数功能一致,第一个传入一个UTF-8编码格式的字符串,这个字符串就是json文件的路径,如果读取成功,则通过root返回,最后的collectCommets默认为true,是用于记录是否保留注释的。我们以默认的Reader()构造的reader对象,这里就是true,如果构造Reader时使用带有Feature版本的,将allowComments_设置为false,那么这里将不会默认保留。第二个parse接受的是两个指针,这两个指针分别指向一个UTF-8编码格式的字符串的起始位置和结束位置,同第一个一样,只是另外一种形式,它也主要是供第一个parse内部调用使用。第三个parse接受一个输入流,也就是C++中用于读取文件的流,功能与前两个相同。

        parse的实现如下所示,主要功能是在parse(beginDoc, endDoc)中实现的,其它两个parse内部实现调用了这个parse。

bool parse(const char* beginDoc, const char* endDoc, Value& root, bool collectComments = true)的实现:
bool Reader::parse(const char* beginDoc,
                   const char* endDoc,
                   Value& root,
                   bool collectComments) {
  if (!features_.allowComments_) {
    collectComments = false;
  }

  begin_ = beginDoc;
  end_ = endDoc;
  collectComments_ = collectComments;
  current_ = begin_;
  lastValueEnd_ = nullptr;
  lastValue_ = nullptr;
  commentsBefore_.clear();
  errors_.clear();
  while (!nodes_.empty())
    nodes_.pop();
  nodes_.push(&root);

  bool successful = readValue();
  Token token;
  skipCommentTokens(token);
  if (collectComments_ && !commentsBefore_.empty())
    root.setComment(commentsBefore_, commentAfter);
  if (features_.strictRoot_) {
    if (!root.isArray() && !root.isObject()) {
      // Set error location to start of doc, ideally should be first token found
      // in doc
      token.type_ = tokenError;
      token.start_ = beginDoc;
      token.end_ = endDoc;
      addError(
          "A valid JSON document must be either an array or an object value.",
          token);
      return false;
    }
  }
  return successful;
}

bool parse(const std::string& document, Value& root, bool collectComments = true)的实现:
bool Reader::parse(const std::string& document,
                   Value& root,
                   bool collectComments) {
  document_.assign(document.begin(), document.end());
  const char* begin = document_.c_str();
  const char* end = begin + document_.length();
  return parse(begin, end, root, collectComments);
}


bool parse(IStream& is, Value& root, bool collectComments = true)的实现:
bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
  String doc;
  std::getline(is, doc, (char)EOF);
  return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
}

        在这个源码中,首先先对一些标志位进行了初始化,将一些用于存储的指针都置空,将用于记录JSON对象的栈清空,然后直接调用了readValue进行读取,读取成功后,如果我们正常使用,不设置feature特性,则读取成功就完毕了,Value& root中就有了我们需要的JSON对象。如果设置了feature特性,则会根据所设置的特性做一些逻辑判断给出读取结果。而另外两个parse函数中,首先,string参数的那个parse实现,就是把string字符串的开始和结尾转换成char*型的begin和end再调用刚才的那个parse,实现相同。IStream参数的parse实现,是通过直接把打开的文件中的内容按行读取到一个string中,然后调用string版本的parse进行处理,实现也是相同的。所以,这里最主要的操作就是这个readValue()函数,简要看一下它的实现:

bool Reader::readValue() {
  // readValue() may call itself only if it calls readObject() or ReadArray().
  // These methods execute nodes_.push() just before and nodes_.pop)() just
  // after calling readValue(). parse() executes one nodes_.push(), so > instead
  // of >=.
  if (nodes_.size() > stackLimit_g)
    throwRuntimeError("Exceeded stackLimit in readValue().");

  Token token;
  skipCommentTokens(token);
  bool successful = true;

  if (collectComments_ && !commentsBefore_.empty()) {
    currentValue().setComment(commentsBefore_, commentBefore);
    commentsBefore_.clear();
  }

  switch (token.type_) {
  case tokenObjectBegin:
    successful = readObject(token);
    currentValue().setOffsetLimit(current_ - begin_);
    break;
  case tokenArrayBegin:
    successful = readArray(token);
    currentValue().setOffsetLimit(current_ - begin_);
    break;
  case tokenNumber:
    successful = decodeNumber(token);
    break;
  case tokenString:
    successful = decodeString(token);
    break;
  case tokenTrue: {
    Value v(true);
    currentValue().swapPayload(v);
    currentValue().setOffsetStart(token.start_ - begin_);
    currentValue().setOffsetLimit(token.end_ - begin_);
  } break;
  case tokenFalse: {
    Value v(false);
    currentValue().swapPayload(v);
    currentValue().setOffsetStart(token.start_ - begin_);
    currentValue().setOffsetLimit(token.end_ - begin_);
  } break;
  case tokenNull: {
    Value v;
    currentValue().swapPayload(v);
    currentValue().setOffsetStart(token.start_ - begin_);
    currentValue().setOffsetLimit(token.end_ - begin_);
  } break;
  case tokenArraySeparator:
  case tokenObjectEnd:
  case tokenArrayEnd:
    if (features_.allowDroppedNullPlaceholders_) {
      // "Un-read" the current token and mark the current value as a null
      // token.
      current_--;
      Value v;
      currentValue().swapPayload(v);
      currentValue().setOffsetStart(current_ - begin_ - 1);
      currentValue().setOffsetLimit(current_ - begin_);
      break;
    } // Else, fall through...
  default:
    currentValue().setOffsetStart(token.start_ - begin_);
    currentValue().setOffsetLimit(token.end_ - begin_);
    return addError("Syntax error: value, object or array expected.", token);
  }

  if (collectComments_) {
    lastValueEnd_ = current_;
    lastValue_ = &currentValue();
  }

  return successful;
}

        readValue中根据当前文本的类型,如object型(带有{})、array型(带有[])、Number型(数字)、String型(字符等)、Bool型(true/false)、Null型(空)甚至是其他类型(解析不了的syntax error),走相应不同分支的实现函数,每个分支的实现函数中,实现了各自类型的读取方法,这些分支函数具体有如下这些:

bool readToken(Token& token);
  void skipSpaces();
  bool match(Location pattern, int patternLength);
  bool readComment();
  bool readCStyleComment();
  bool readCppStyleComment();
  bool readString();
  void readNumber();
  bool readValue();
  bool readObject(Token& token);
  bool readArray(Token& token);
  bool decodeNumber(Token& token);
  bool decodeNumber(Token& token, Value& decoded);
  bool decodeString(Token& token);
  bool decodeString(Token& token, String& decoded);
  bool decodeDouble(Token& token);
  bool decodeDouble(Token& token, Value& decoded);
  bool decodeUnicodeCodePoint(Token& token,
                              Location& current,
                              Location end,
                              unsigned int& unicode);
  bool decodeUnicodeEscapeSequence(Token& token,
                                   Location& current,
                                   Location end,
                                   unsigned int& unicode);
  bool addError(const String& message, Token& token, Location extra = nullptr);
  bool recoverFromError(TokenType skipUntilToken);
  bool addErrorAndRecover(const String& message,
                          Token& token,
                          TokenType skipUntilToken);
  void skipUntilSpace();
  Value& currentValue();
  Char getNextChar();
  void
  getLocationLineAndColumn(Location location, int& line, int& column) const;
  String getLocationLineAndColumn(Location location) const;
  void addComment(Location begin, Location end, CommentPlacement placement);
  void skipCommentTokens(Token& token);

  static bool containsNewLine(Location begin, Location end);
  static String normalizeEOL(Location begin, Location end);

        这其中的判断条件比较冗杂,且属于对用户而言不重要的内容,从名字中都基本都能够看出功能,readXXX的函数多数是用来读取,decodeXXX的函数是用来从read出来的东西中解析所需的类型的,其余如addXXX是用来记录一些过程中的错误或注释等,还有一些是用于获取当前读取的指针位置、判断条件等等,具体的实现就不展开了。

        那么,在Reader类的使用上,举个示例如下所示:

//字符串读取
int readFromString()
{
    Json::Value root;
    Json::Reader reader;
    const char* json_document = "{\"age\" : 26,\"name\" : \"Hearz\", \"blog\" : \"https://mp.csdn.net/postedit?not_checkout=1\", \"isVisit\" : true}";
    if(!reader.parse(json_document, root))
    {
        std::cout << "parse string error!" << std::endl;
        return -1;
    }

    std::cout << "root : " << root.toStyledString() << std::endl;

    return 0;
}

//从json文件读取
int readFromFile()
{
    Json::Value root;
    Json::Reader reader;
    std::string fileName = "./jsonFile.json";
    std::ifstream is(fileName, std::ios::binary);
    if(!reader.parse(is, root))
    {
        std::cout << "parse string error!" << std::endl;
        return -1;
    }
    std::cout << "root : " << root.toStyledString() << std::endl;

    is.close();
    return 0;
}

        Reader类一般正常使用主要就是以上这些,其他还有一些使用不太常见,就不一一举例了。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值