首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用jsoncpp解析JSON

使用jsoncpp解析JSON
EN

Code Review用户
提问于 2016-03-11 20:20:01
回答 1查看 11.4K关注 0票数 4

我把问题战略性地留在评论中,因为我认为用这种方式回答问题更容易。

基本上,我的小类所做的就是抽象出最初在函数中的功能。

这个类读取json格式的文件,并创建一个可以用来遍历json的对象。我明白,如果某件事可以变成一个函数,它不应该变成一个类,但我需要练习。

下面我概述了我要找的东西。选择一个或者全部选择。

寻找:

  • 我是否正确地使用了"&“
  • 正确的#include头文件
  • 初始化对象的方法如下:Root rate("test.json", ["query"]["results"]["rate"]) (语法可以不同)
  • 最佳做法咨询意见

不寻找(至少还没有):

  • 异常和错误处理
  • C程序员的建议

root.h

代码语言:javascript
复制
#ifndef ROOT_H
#define ROOT_H

// Should header files always be included in root.h as opposed to root.cpp?
#include <fstream>
#include <string>   // Seems like I do not need to include this string container, why?
                    //is it because json/json.h contains it?


#include "json/json.h" // Would I ever create a class with a dependency like this one?
                       // jsoncpp.sourceforge.net/annotated.html

class Root
{
private:
    std::ifstream m_json;

public:
    Json::Value m_root;
    Json::Value m_query;

    Root(const std::string&);
    ~Root();
};

#endif // ROOT_H

root.cpp

代码语言:javascript
复制
#include "root.h"

Root::Root(const std::string & filename) : m_json(filename, std::ifstream::binary)
// Is std::ifstream::binary ok to put it in here like this ^?
// It's working, but would that be good practice?
{
    m_json >> m_root;
    m_json.close();  // Do I need .close() here?

}
Root::~Root(){}

main.cpp

代码语言:javascript
复制
#include <iostream>
#include "root.h"

int main()
{

    Root rate("test.json");
    rate.m_query = rate.m_root["query"]["items"]; // Is it ok to assign member to a member like so,
                                                  // as opposed to just a variable?

    // How can I instantiate my object like the line below?
    // Root rate("test.json", ["query"]["results"]["rate"]);
    // Syntax does not have to match precisely?


    for(const auto & it : rate.m_query)
    {
        std::cout << it << std::endl;
    }
}

test.json

代码语言:javascript
复制
{
  "query": {
    "count": 3,
    "items": [
      {"item": "4"},
      {"item": "3"},
      {"item": "2"}
    ]
  }
EN

回答 1

Code Review用户

回答已采纳

发布于 2016-03-11 21:53:27

//应该始终将头文件包含在root.h中,而不是

您应该包含所需的所有头文件(不超过所需)。

在您的示例中,您将在头文件中使用以下类型。

代码语言:javascript
复制
std::ifstream
std::string
Json::Value

因此,您应该为这些类型包括适当的头文件。

代码语言:javascript
复制
#include <fstream>
#include <string>
#include "json/json.h"

唯一的曲线球是,如果您只使用类型引用,那么从技术上讲,您可以使用前向声明(而不是包含头文件)。所以您可以使用std::string的前向声明,不幸的是,它不是您的类,而且您实际上不知道如何转发声明它,因为这不是在标准中定义的,所以您必须包含头文件。

//似乎不需要包含这个字符串容器,为什么?

它可以通过<fstream>"json/json.h"间接包括在内。但是您仍然应该包含<string>头文件,因为这些依赖项可能并不总是有效的(在不同的平台或编译器的不同版本上,它可能包含不同的内容)。因此,不要假设因为它在这个平台上工作,它将始终工作。考虑最坏的情况并显式地包括<string>

“包含保护”似乎有点过于通用:

代码语言:javascript
复制
ROOT_H

看来其他人可能会用这个。你需要确保你的卫兵是独一无二的。我总是使用包含名称空间的警卫。

代码语言:javascript
复制
// I own the domain name thorsanvil.com
// and I put all my classes in the namespace ThorsAnvil
// So my include guards go like this.
#define THORSANVIL_<Optional Nested Namespace>_ROOT_H

// std::ifstream::binary是否可以像^这样把它放在这里? //它起作用了,但这是一个好做法吗?

好的。完全没问题。

您只需理解它的含义以及二进制模式和文本模式(默认模式)之间的区别。在文本模式下:=>“特定于平台的”“行尾序列”在读取文件时被转换为'\n'。在二进制模式下:不执行转换,您将得到原始字节。

因此,在构造函数中:

代码语言:javascript
复制
Root::Root(const std::string & filename) : m_json(filename, std::ifstream::binary)

{
    m_json >> m_root;
    m_json.close();  // Do I need .close() here?

                     // Yes probably.
                     // Closing it releases the related OS resources.
                     // If you don't the resource will be held until
                     // the whole object is destroyed.

}

我不认为m_json对象需要成为该对象的一部分。它只会在构造函数中使用。一旦加载了数据,就不会再使用它了。因此,只需将其声明为构造函数的本地自动变量即可。

代码语言:javascript
复制
Root::Root(const std::string & filename)
{
    std::ifstream  m_json(filename, std::ifstream::binary)

    m_json >> m_root;    
}
// Now we don't need `close()` because the object goes out of scope
// and the `std::ifstream` destructor calls close for us.

//是否可以将成员分配给这样的成员,即 //,而不仅仅是变量?

它是。但为什么。使用局部变量似乎更符合逻辑。

代码语言:javascript
复制
Json::Value query = rate.m_root["query"]["items"];

更喜欢'\n'而不是std::endl

不同之处在于,std::endl强制缓冲区刷新。缓冲区需要时会自动刷新。强迫它刷新只会使您的代码效率低下。

//如何像下面的行那样实例化我的对象? //根率(“test.json”,“查询”“费率”);

您可以定义operator[]。你可以用一个字符串作为参数。

代码语言:javascript
复制
class Root
{
    public:
        JsonValue operator[](std::string const& index)
        {
            return m_root[index];
        }
        // Other stuff in your class.
};

现在,它可以用于:

代码语言:javascript
复制
Root    json("file.name");
Json::Value = json["query"]["results"]["rate"];
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/122602

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档