rapidjson常見使用示例

一見發表於2019-01-28

目錄

目錄 1

1. 前言 2

2. Move語意 2

3. rapidjson::Document 2

4. 成員迭代器MemberIterator 3

5. 陣列迭代器ValueIterator 4

6. #include標頭檔案 4

7. 示例1:解析一個字串 4

8. 示例2:構造一個json並轉成字串 6

9. 示例3:修改一個已有的json字串 7

10. 示例4:讀陣列 8

11. 示例5: 以Writer構造一個json,然後修改它,最後轉成字串 8

12. 示例6: 以Document構造一個json,然後修改它,最後轉成字串 9

13. 示例7: 以Document構造一個json,然後修改它,最後轉成字串 11

14. 示例8:構造空物件和陣列 12

15. 示例9:刪除陣列元素 13

16. 示例10:不轉義中文 13

17. 示例11:schema使用示例 14

18. 示例12:schema完整示例 16

19. FindMember整數值 17

20. FindMember字串值 17

21. 遍歷成員 18

22. 遍歷陣列1:字串陣列 18

23. 遍歷陣列2:一級物件陣列 18

24. 遍歷陣列3:兩級物件陣列 19

25. 輔助函式1:任意型別都以字串返回 20

26. 輔助函式2:取int32_t值 21

27. 輔助函式3:取int64_t值 21

28. 輔助函式4:取uint32_t值 22

29. 輔助函式5:取uint64_t值 23

30. 輔助函式6:物件轉字串 23

31. 輔助函式7:字串轉物件 24

32. rapidjson的“坑” 24

 

1. 前言

rapidjson相比jsoncpp效能高出太多,使用介面一樣的簡單的。官方中文幫助文件:http://rapidjson.org/zh-cn/

2. Move語意

rapidjson的Move語意,請瀏覽:

http://rapidjson.org/zh-cn/md_doc_tutorial_8zh-cn.html#MoveSemantics

示例:

rapidjson::Value a(123);

rapidjson::Value b(456);

b = a; // a變成Null,b變成數字123,這樣的做法是基於效能考慮

 

除了上述示例的複製語句外,AddMember()和PushBack()也採用了Move語意。深複製Value:

Value v1("foo");

// Value v2(v1); // 不容許

Value v2(v1, a); // 製造一個克隆,v1不變

 

Document d;

v2.CopyFrom(d, a); // 把整個document複製至v2,d不變

 

rapidjson為了最大化效能,大量使用了淺拷貝,使用之前一定要了解清楚。如果採用了淺拷貝,特別要注意區域性物件的使用,以防止物件已被析構了,卻還在被使用。

3. rapidjson::Document

特別注意rapidjson::Document可以為object、array、number、string、boolean和null中任意一種型別。只有為object時才可以呼叫HasMember等與object有關的方法。

#include <rapidjson/document.h>

#include <rapidjson/error/en.h>

#include <rapidjson/stringbuffer.h>

#include <rapidjson/writer.h>

#include <stdio.h>

 

int main(int argc, char* argv[])

{

    std::string str;

    rapidjson::Document doc;

    doc.Parse(argv[1]);

    if (doc.HasParseError())

        printf("parse error\n");

        

    // 注意doc可為object, array, number, string, boolean, null中任意一種型別

    if (!doc.IsObject())

        printf("not object\n");

    else

    {

        printf("parse ok\n");

        if (doc.IsNumber())

        printf("%d\n", doc.GetInt());

        

        // doc為object型別時,才能呼叫HasMember

        if (doc.HasMember("x"))

            printf("has x\n");

        else

            printf("without x\n");

    }

 

    return 0;

}

4. 成員迭代器MemberIterator

成員迭代器rapidjson::Value::MemberIterator實際指向GenericMember:

template <typename Encoding, typename Allocator> 

struct GenericMember 

    // 成員名,只能為string值

    GenericValue<Encoding, Allocator> name;

    // 成員值,可為各類型別,如字串、陣列、子物件等

    GenericValue<Encoding, Allocator> value;

};

 

typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator;

class GenericMemberIterator {

    typedef GenericMember<Encoding,Allocator> PlainType;

    typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;

    typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;

    typedef GenericMemberIterator Iterator;

    // Pointer to (const) GenericMember

    typedef typename BaseType::pointer Pointer;

    Pointer ptr_; // raw pointer

    Pointer   operator->() const { return ptr_; }

};

5. 陣列迭代器ValueIterator

陣列迭代器ValueIterator實際為GenericValue指標:

typedef GenericValue* ValueIterator;

typedef const GenericValue* ConstValueIterator;

6. #include標頭檔案

// 需要#include的標頭檔案:

#include <rapidjson/document.h>

#include <rapidjson/error/en.h>

#include <rapidjson/stringbuffer.h>

#include <rapidjson/writer.h>

 

其中en為english的簡寫,定義了取出錯資訊的函式GetParseError_En(errcode)。

7. 示例1:解析一個字串

1) 執行輸出結果

count=2

name=zhangsan

name=wangwu

 

2) 示例程式碼

void x1()

{

    rapidjson::Document document; // 定義一個Document物件

    std::string str = "{\"count\":2,\"names\":[\"zhangsan\",\"wangwu\"]}";

 

    document.Parse(str.c_str()); // 解析,Parse()無返回值,也不會拋異常

    if (document.HasParseError()) // 通過HasParseError()來判斷解析是否成功

    {

        // 可通過GetParseError()取得出錯程式碼,

        // 注意GetParseError()返回的是一個rapidjson::ParseErrorCode型別的列舉值

        // 使用函式rapidjson::GetParseError_En()得到錯誤碼的字串說明,這裡的En為English簡寫

        // 函式GetErrorOffset()返回出錯發生的位置

        printf("parse error: (%d:%d)%s\n", document.GetParseError(), document.GetErrorOffset(), rapidjson::GetParseError_En(document.GetParseError()));

    }

    else

    {

        // 判斷某成員是否存在

        if (!document.HasMember("count") || !document.HasMember("names"))

        {

            printf("invalid format: %s\n", str.c_str());

        }

        else

        {

            // 如果count不存在,則執行程式會掛,DEBUG模式下直接abort

            rapidjson::Value& count_json = document["count"];

            

            // 如果count不是整數型別,呼叫也會掛,DEBUG模式下直接abort

            // GetInt()返回型別為int

            // GetUint()返回型別為unsigned int

            // GetInt64()返回型別為int64_t

            // GetUint64()返回型別為uint64_t

            // GetDouble()返回型別為double

            // GetString()返回型別為char*

            // GetBool()返回型別為bool

            int count = count_json.GetInt();

            printf("count=%d\n", count);

            

            // 方法GetType()返回列舉值: kNullType,kFalseType,kTrueType,kObjectType,kArrayType,kStringType,kNumberType

            // 可用IsArray()判斷是否為陣列,示例: { "a": [1, 2, 3, 4] }

            // 用IsString()判斷是否為字串值

            // 用IsDouble()判斷是否為double型別的值,示例: { "pi": 3.1416 }

            // 用IsInt()判斷是否為int型別的值

            // 用IsUint()判斷是否為unsigned int型別的值

            // 用IsInt64()判斷是否為int64_t型別的值

            // 用IsUint64()判斷是否為uint64_t型別的值

            // 用IsBool()判斷是否為bool型別的值

            // 用IsFalse()判斷值是否為false,示例: { "t": true, "f": false }

            // 用IsTrue()判斷值是否為true

            // 用IsNull()判斷值是否為NULL,示例: { "n": null }

            // 更多說明可瀏覽:

            // https://miloyip.gitbooks.io/rapidjson/content/zh-cn/doc/tutorial.zh-cn.html

 

            const rapidjson::Value& names_json = document["names"];

            for (rapidjson::SizeType i=0; i<names_json.Size(); ++i)

            {

                std::string name = names_json[i].GetString();

                printf("name=%s\n", name.c_str());

            }

        }

    }

}

8. 示例2:構造一個json並轉成字串

1) 執行輸出結果

{"count":2,"names":[{"name":"zhangsan"},{"name":"wangwu"}]}

 

2) 示例程式碼

void x2()

{

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

 

    writer.StartObject();

 

    // count

    writer.Key("count");

    writer.Int(2);

    // 寫4位元組有符號整數: Int(int32_t x)

    // 寫4位元組無符號整數: Uint(uint32_t x)

    // 寫8位元組有符號整數: Int64(int64_t x)

    // 寫8位元組無符號整數: Uint64(uint64_t x)

    // 寫double值: Double(double x)

    // 寫bool值: Bool(bool x)

 

    // names

    writer.Key("names");

    writer.StartArray();

 

    writer.StartObject();

    writer.Key("name");

    writer.String("zhangsan");

    writer.EndObject();

 

    writer.StartObject();

    writer.Key("name");

    writer.String("wangwu");

    writer.EndObject();

 

    writer.EndArray();

    writer.EndObject();

 

    // 以字串形式列印輸出

    printf("%s\n", buffer.GetString());

}

9. 示例3:修改一個已有的json字串

1) 執行輸出結果

{"name":"wangwu","age":22}

 

2) 示例程式碼

void x3()

{

    rapidjson::Document document;

    std::string str = "{\"name\":\"zhangsan\",\"age\":20}";

    document.Parse(str.c_str());

 

    rapidjson::Value& name_json = document["name"];

    rapidjson::Value& age_json = document["age"];

    std::string new_name = "wangwu";

    int new_age = 22;

 

    // 注意第三個引數是document.GetAllocator(),相當於深拷貝,rapidjson會分配一塊記憶體,然後複製new_name.c_str(),

    // 如果不指定第三個引數,則是淺拷貝,也就是rapidjson不會分配一塊記憶體,而是直接指向new_name.c_str(),省去複製提升了效能

    // 官方說明:

    // http://rapidjson.org/zh-cn/md_doc_tutorial_8zh-cn.html#CreateString

    name_json.SetString(new_name.c_str(), new_name.size(), document.GetAllocator());

    age_json.SetInt(new_age);

 

    // 轉成字串輸出

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

    document.Accept(writer);

    printf("%s\n", buffer.GetString());

}

10. 示例4:讀陣列

1) 執行輸出結果

zhangsan wangwu

 

2) 示例程式碼

void x4()

{

    rapidjson::Document document;

    std::string str = "{\"count\":2,\"names\":[{\"name\":\"zhangsan\"},{\"name\":\"wangwu\"}]}";

 

    document.Parse(str.c_str());

    if (document.HasParseError())

    {

        printf("parse error: %d\n", document.GetParseError());

    }

    else

    {

        rapidjson::Value& names_json = document["names"];

        for (rapidjson::SizeType i=0; i<names_json.Size(); ++i)

        {

            if (names_json[i].HasMember("name"))

            {

                rapidjson::Value& name_json = names_json[i]["name"];

                printf("%s ", name_json.GetString());

            }

        }

        printf("\n");

    }

}

11. 示例5: 以Writer構造一個json,然後修改它,最後轉成字串

1) 執行輸出結果

{"count":2}

{"count":8}

 

2) 示例程式碼

void x5()

{

    rapidjson::StringBuffer buffer1;

    rapidjson::Writer<rapidjson::StringBuffer> writer1(buffer1);

    

    writer1.StartObject();

    writer1.Key("count");

    writer1.Int(2);    

    writer1.EndObject();

    printf("%s\n", buffer1.GetString());

 

    // 轉成Document物件

    rapidjson::Document document;

    document.Parse(buffer1.GetString());

 

    // 修改

    rapidjson::Value& count_json = document["count"];

    count_json.SetInt(8);

    

    // 轉成字串

    rapidjson::StringBuffer buffer2;

    rapidjson::Writer<rapidjson::StringBuffer> writer2(buffer2);

 

    document.Accept(writer2);

    printf("%s\n", buffer2.GetString());

}

12. 示例6: 以Document構造一個json,然後修改它,最後轉成字串

1) 執行輸出結果

{"count":3,"names":[{"id":1,"name":"zhangsan"}]}

{"count":9,"names":[{"id":1,"name":"zhangsan"}]}

 

2) 示例程式碼

void x6()

{

    rapidjson::Document document;

    std::string str = "{}"; // 這個是必須的,且不能為"",否則Parse出錯

    document.Parse(str.c_str());

 

    // 新增成員count

    // AddMember第一個引數可以為字串常,如“str”,不能為“const char*”和“std::string”,

    // 如果使用“const char*”,則需要使用StringRefType轉換:StringRefType(str.c_str())

    document.AddMember("count", 3, document.GetAllocator());

 

    // 新增陣列成員

    rapidjson::Value array(rapidjson::kArrayType);

    rapidjson::Value object(rapidjson::kObjectType); // 陣列成員

    object.AddMember("id", 1, document.GetAllocator()); 

    object.AddMember("name", "zhangsan", document.GetAllocator()); 

    

    // 如果陣列新增無名字的成員,定義Value時應當改成相應的型別,如:

    //rapidjson::Value value(rapidjson::kStringType);

    //rapidjson::Value value(rapidjson::kNumberType);

    //rapidjson::Value value(rapidjson::kFalseType);

    //rapidjson::Value value(rapidjson::kTrueType);

    //array.PushBack(value, document.GetAllocator());

    //效果將是這樣:'array':[1,2,3,4,5]

    

    // 注意下面用法編譯不過:

    //std::string str1 = "hello";

    //object.AddMember("name", str1.c_str(), document.GetAllocator());

    //const char* str2 = "hello";

    //object.AddMember("name", str2, document.GetAllocator());

    //

    // 下面這樣可以:

    //object.AddMember("name", "hello", document.GetAllocator());

    //const char str3[] = "hello";

    //object.AddMember("name", str3, document.GetAllocator());

    //    

    //std::string str4 = "#####";

    //rapidjson::Value v(str4.c_str(), document.GetAllocator());

    //obj.AddMember("x", v, document.GetAllocator());

    // 上面兩行也可以寫在一行:

    //obj.AddMember("x", rapidjson::Value(str4.c_str(), document.GetAllocator()).Move(), document.GetAllocator());

 

    // 新增到陣列中

    array.PushBack(object, document.GetAllocator());

    // 新增到document中

    document.AddMember("names", array, document.GetAllocator());

 

    // 轉成字串輸出

    rapidjson::StringBuffer buffer1;

    rapidjson::Writer<rapidjson::StringBuffer> writer1(buffer1);

    document.Accept(writer1);

    printf("%s\n", buffer1.GetString());

    

    // 修改值

    rapidjson::Value& count_json = document["count"];

    count_json.SetInt(9);

 

    // 再次輸出

    rapidjson::StringBuffer buffer2;

    rapidjson::Writer<rapidjson::StringBuffer> writer2(buffer2);

    document.Accept(writer2);

    printf("%s\n", buffer2.GetString());

}

13. 示例7: 以Document構造一個json,然後修改它,最後轉成字串

1) 執行輸出結果(不轉義就輸出)

x7=>

{"title":"\u8D2B\u56F0\u5B64\u513F\u52A9\u517B"}

 

2) 示例程式碼

void x7()

{

    std::string root = "{}";

    rapidjson::Document document;

    document.Parse(root.c_str());

 

    std::string title = "\u8D2B\u56F0\u5B64\u513F\u52A9\u517B";

    document.AddMember("title", rapidjson::Value(title.c_str(), document.GetAllocator()).Move(), document.GetAllocator());

 

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::ASCII<> > writer(buffer);

    // 如果上面一句改成普通的:

    // rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

    // 則輸出將變成:

    // x7=>

    // 貧困孤兒助養

    

    document.Accept(writer);

    printf("x7=>\n%s\n", buffer.GetString());

}

14. 示例8:構造空物件和陣列

1) 執行輸出結果

{"age":{},"times":{},"names":[],"urls":[],"books":[]}

{"age":6,"times":{},"names":[],"urls":[],"books":[]}

 

2) 示例程式碼

void x8()

{

    rapidjson::Document document;

    document.Parse("{}"); // 這裡換成document.SetObject()也可以

 

    // 下面為2種構造空物件的方法

    document.AddMember("age", rapidjson::Value(rapidjson::kObjectType).Move(), document.GetAllocator());

    document.AddMember("times", rapidjson::Value().SetObject(), document.GetAllocator());

 

    // 下面為2種構造空陣列的方法

    document.AddMember("names", rapidjson::Value(rapidjson::kArrayType).Move(), document.GetAllocator());

    document.AddMember("urls", rapidjson::Value(rapidjson::kArrayType).Move(), document.GetAllocator());

    document.AddMember("books", rapidjson::Value().SetArray(), document.GetAllocator());

 

    rapidjson::StringBuffer buffer1;

    rapidjson::Writer<rapidjson::StringBuffer> writer1(buffer1);

    document.Accept(writer1);

    printf("%s\n", buffer1.GetString());

 

    rapidjson::Value& age = document["age"];

    age.SetInt(6);

 

    rapidjson::StringBuffer buffer2;

    rapidjson::Writer<rapidjson::StringBuffer> writer2(buffer2);

    document.Accept(writer2);

    printf("%s\n", buffer2.GetString());

}

15. 示例9:刪除陣列元素

1) 執行輸出結果

{ "names": [ {"name":"zhangsan","age":100}, {"name":"wangwu","age":90}, {"name":"xiaozhang","age":20} ]}

{"names":[{"name":"zhangsan","age":100},{"name":"wangwu","age":90}]}

 

2) 示例程式碼

void x9()

{

    std::string str = "{ \"names\": [ {\"name\":\"zhangsan\",\"age\":100}, {\"name\":\"wangwu\",\"age\":90}, {\"name\":\"xiaozhang\",\"age\":20} ]}";

    

    rapidjson::Document document;

    document.Parse(str.c_str());

    

    rapidjson::Value& names_json = document["names"];

    for (rapidjson::Value::ValueIterator iter=names_json.Begin(); iter!=names_json.End();)

    {

        std::string name = (*iter)["name"].GetString();

        

        // 不要小張了

        if (name == "xiaozhang")

            iter = names_json.Erase(iter);

        else

            ++iter;

    }

    

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

    document.Accept(writer);

    

    printf("%s\n", str.c_str());

    printf("%s\n", buffer.GetString());

}

16. 示例10:不轉義中文

1) 執行輸出結果

{"title":"貧困孤兒助養"}

{"title":"\u8D2B\u56F0\u5B64\u513F\u52A9\u517B"}

 

2) 示例程式碼

//g++ -g -o b b.cpp -I/usr/local/thirdparty/rapidjson/include

#include <rapidjson/document.h>

#include <rapidjson/stringbuffer.h>

#include <rapidjson/writer.h>

#include <string>

#include <stdio.h>

 

int main()

{

    std::string str = "{\"title\":\"\u8d2b\u56f0\u5b64\u513f\u52a9\u517b\"}";

    rapidjson::Document document;

    document.Parse(str.c_str());

    if (document.HasParseError())

    {

        printf("parse %s failed\n", str.c_str());

        exit(1);

    }

 

    rapidjson::StringBuffer buffer1;

    rapidjson::Writer<rapidjson::StringBuffer> writer1(buffer1);

    document.Accept(writer1);

    printf("%s\n", buffer1.GetString());

 

    rapidjson::StringBuffer buffer2;

    rapidjson::Writer<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::ASCII<> > writer2(buffer2);

    document.Accept(writer2);

    printf("%s\n", buffer2.GetString());

 

    return 0;

}

17. 示例11:schema使用示例

json的schema用來檢驗json資料,它也採用了json格式。

1) 示例程式碼

rapidjson::Document schema_document;

schema_document.Parse(schema.c_str());

 

if (!schema_document.HasParseError())

{

    rapidjson::Document document;

    document.Parse(str.c_str());

    

    if (!document.HasParseError())

    {

        SchemaDocument schema(schema_document);

        SchemaValidator validator(schema);

        if (!document.Accept(validator))

        {

             // 檢驗出錯,輸出錯誤資訊

             StringBuffer sb;

             validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);

             

             printf("Invalid schema: %s\n", sb.GetString());

             printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());

             

             sb.Clear();

             validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);

             printf("Invalid document: %s\n", sb.GetString());

        }

    }

}

 

2) 示例json

{

    "id": 1,

    "name": "A green door",

    "price": 12.50,

    "tags": ["home", "green"]

}

 

3) 上段json對應的schema

{

    "$schema": "http://json-schema.org/draft-04/schema#",

    "title": "Product",

    "description": "A product from Acme's catalog",

    "type": "object",

    

    "properties": {

        "id": {

            "description": "The unique identifier for a product",

            "type": "integer"

        },

        "name": {

            "description": "Name of the product",

            "type": "string"

        },

        "price": {

            "type": "number",

            "minimum": 0,

            "exclusiveMinimum": true

        },

        "tags": {

            "type": "array",

            "items": {

                "type": "string"

            },

            "minItems": 1,

            "uniqueItems": true

        }

    },

    "required": ["id", "name", "price"]

}

 

“title”和“description”是描述性的,可以不寫。$schema也是可選的,依據的是《JSON Schema Draft v4》。

18. 示例12:schema完整示例

#include <rapidjson/document.h>

#include <rapidjson/schema.h>

#include <rapidjson/stringbuffer.h>

 

int main()

{

    std::string str = "\{\"aaa\":111,\"aaa\":222}"; // "\{\"aaa\":111,\"a\":222}"

#if 0

    std::string schema_str = "{\"type\":\"object\",\"properties\":{\"aaa\":{\"type\":\"integer\"},\"bbb\":{\"type\":\"string\"}},\"required\":[\"aaa\",\"bbb\"]}";

#else

    std::string schema_str = "{\"type\":\"object\",\"properties\":{\"aaa\":{\"type\":\"integer\"},\"bbb\":{\"type\":\"integer\"}},\"required\":[\"aaa\",\"bbb\"]}";

#endif

    printf("%s\n", str.c_str());

    printf("%s\n", schema_str.c_str());

 

    rapidjson::Document doc;

    rapidjson::Document schema_doc;

 

    schema_doc.Parse(schema_str.c_str());

    doc.Parse(str.c_str());

 

    rapidjson::SchemaDocument schema(schema_doc);

    rapidjson::SchemaValidator validator(schema);

    if (doc.Accept(validator))

    {

        printf("data ok\n");

    }

    else

    {

        rapidjson::StringBuffer sb;

        validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);

 

        printf("Invalid schema: %s\n", sb.GetString());

        printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());

 

        sb.Clear();

        validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);

        printf("Invalid document: %s\n", sb.GetString());

    }

 

    return 0;

}

19. FindMember整數值

int age;

const rapidjson::Value::ConstMemberIterator iter =

    doc.FindMember("age");

if (iter!=doc.MemberEnd() && iter->value.IsInt())

    age = iter->value.GetInt();

20. FindMember字串值

std::string name;

const rapidjson::Value::ConstMemberIterator iter =

    doc.FindMember("name");

if (iter!=doc.MemberEnd() && iter->value.IsString())

    name.assign(iter->value.GetString(), iter->value.GetStringLength());

21. 遍歷成員

rapidjson::Value value;

。。。

for (rapidjson::Value::ConstMemberIterator iter=value.MemberBegin();

    iter!=value.MemberEnd(); ++iter)

{

    const rapidjson::Value& name_json = iter->name; // 這個必是字串

    const rapidjson::Value& value_json = iter->value; // 這個可以為物件、陣列等

    printf("%s\n", name_json.GetString());

}

22. 遍歷陣列1:字串陣列

// 示例陣列:

// {"k":["k1","k2","k3"]}

rapidjson::Document doc;

doc.Parse(str.c_str());

 

const rapidjson::Value& k = doc["k"];

// 遍歷陣列

for (rapidjson::Value::ConstValueIterator v_iter=k.Begin();

    v_iter!=k.End(); ++v_iter)

{

    // k1

    // k2

    // k3

    printf("%s\n", (*v_iter).GetString());

}

23. 遍歷陣列2:一級物件陣列

// 陣列示例:

// {"h":[{"k1":"f1"},{"k2":"f2"}]}

rapidjson::Document doc;

doc.Parse(str.c_str());

 

const rapidjson::Value& h = doc["h"];

// 遍歷陣列

for (rapidjson::Value::ConstValueIterator v_iter=h.Begin();

    v_iter!=h.End(); ++v_iter)

{

    const rapidjson::Value& field = *v_iter;

    for (rapidjson::Value::ConstMemberIterator m_iter=field.MemberBegin();

        m_iter!=field.MemberEnd(); ++m_iter) // kf對

    {

        // k1 => f1

        // k2 => f2

        const char* key = m_iter->name.GetString();

        const char* value = m_iter->value.GetString();

        printf("%s => %s\n", key, value);

        break;

    }

}

24. 遍歷陣列3:兩級物件陣列

// 陣列示例:

// {"h":[{"k1":["f1","f2"]},{"k2":["f1","f2"]}]}

rapidjson::Document doc;

doc.Parse(str.c_str());

const rapidjson::Value& h = doc["h"];

// 遍歷第一級陣列

for (rapidjson::Value::ConstValueIterator v1_iter=h.Begin();

    v1_iter!=h.End(); ++v1_iter)

{

    const rapidjson::Value& k = *v1_iter; // k1,k2,k3

    // 成員遍歷

    for (rapidjson::Value::ConstMemberIterator m_iter=k.MemberBegin();

        m_iter!=k.MemberEnd(); ++m_iter)

    {

        const char* node_name = m_iter->name.GetString();

        printf("hk: %s\n", node_name);

                            

        const rapidjson::Value& node = m_iter->value;

        // 遍歷第二級陣列

        for (rapidjson::Value::ConstValueIterator v2_iter=node.Begin();

            v2_iter!=node.End(); ++v2_iter)  

        {

            const char* field = (*v2_iter).GetString();

            printf("field: %s\n", field); // f1,f2,f3

        }

    }

}

25. 輔助函式1:任意型別都以字串返回

// 如果不存在,或者為陣列則返回空字串。

std::string rapidjson_string_value(rapidjson::Value& value, const std::string& name)

{

    if (!value.HasMember(name.c_str()))

        return std::string("");

 

    const rapidjson::Value& child = value[name.c_str()];

    if (child.IsString())

        return child.GetString();

 

    char str[100];

    if (child.IsInt())

    {

        snprintf(str, sizeof(str), "%d", child.GetInt());

    }

    else if (child.IsInt64())

    {

        // 為使用PRId64,需要#include <inttypes.h>,

        // 同時編譯時需要定義巨集__STDC_FORMAT_MACROS

        snprintf(str, sizeof(str), "%"PRId64, child.GetInt64());

    }

    else if (child.IsUint())

    {

        snprintf(str, sizeof(str), "%u", child.GetUint());

    }

    else if (child.IsUint64())

    {

        snprintf(str, sizeof(str), "%"PRIu64, child.GetUint64());

    }

    else if (child.IsDouble())

    {

        snprintf(str, sizeof(str), "%.2lf", child.GetDouble());

    }

    else if (child.IsBool())

    {

        if (child.IsTrue())

            strcpy(str, "true");

        else

            strcpy(str, "false");

    }

    else

    {

        str[0] = '\0';

    }

 

    return str;

}

 

26. 輔助函式2:取int32_t值

當為int32_t值或字串實際為int32_t值時,返回對應的int32_t值,其它情況返回0。

// 當為int32_t值,或字串實際為int32_t值時,返回對應的int32_t值,其它情況返回0

int32_t rapidjson_int32_value(rapidjson::Value& value, const std::string& name)

{

    if (!value.HasMember(name.c_str()))

        return 0;

 

    const rapidjson::Value& child = value[name.c_str()];

    if (child.IsInt())

    {

        return child.GetInt();

    }

    else if (child.IsString())

    {

        return atoi(child.GetString());

    }

 

    return 0;

}

27. 輔助函式3:取int64_t值

當為int64_t值,或字串實際為int64_t值時,返回對應的int64_t值,其它情況返回0。

int64_t rapidjson_int64_value(rapidjson::Value& value, const std::string& name)

{

    if (!value.HasMember(name.c_str()))

        return 0;

 

    const rapidjson::Value& child = value[name.c_str()];

    if (child.IsInt64())

    {

        return child.GetInt64();

    }

    else if (child.IsString())

    {

        return (int64_t)atoll(child.GetString());

    }

 

    return 0;

}

28. 輔助函式4:取uint32_t值

當為uin32_t值,或字串實際為uin32_t值時,返回對應的uin32_t值,其它情況返回0。

uint32_t rapidjson_uint32_value(rapidjson::Value& value, const std::string& name)

{

    if (!value.HasMember(name.c_str()))

        return 0;

 

    const rapidjson::Value& child = value[name.c_str()];

    if (child.IsUint())

    {

        return child.GetUint();

    }

    else if (child.IsString())

    {

        return (uint32_t)atoll(child.GetString());

    }

 

    return 0;

}

29. 輔助函式5:取uint64_t值

當為uin64_t值,或字串實際為uin64_t值時,返回對應的uin64_t值,其它情況返回0。

uint64_t rapidjson_uint64_value(rapidjson::Value& value, const std::string& name)

{

    if (!value.HasMember(name.c_str()))

        return 0;

 

    const rapidjson::Value& child = value[name.c_str()];

    if (child.IsUint64())

    {

        return child.GetUint64();

    }

    else if (child.IsString())

    {

        return (uint64_t)atoll(child.GetString());

    }

 

    return 0;

}

30. 輔助函式6:物件轉字串

 

std::string& to_string(const rapidjson::Value& value, std::string* str)

{

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

 

    value.Accept(writer);

    str->assign(buffer.GetString(), buffer.GetSize());

    return *str;

}

 

std::string to_string(const rapidjson::Value& value)

{

    std::string str;

    to_string(value, &str);

 

#if __cplusplus < 201103L

    return str;

#else

    return std::move(str);

#endif // __cplusplus < 201103L

}

31. 輔助函式7:字串轉物件

bool to_rapidjson(const std::string& str, rapidjson::Document* doc)

{

    doc->Parse(str.c_str());

    return !doc->HasParseError();

}

 

void to_rapidjson(const std::string& str, rapidjson::Document& doc)

{

    doc.Parse(str.c_str());

    if (doc.HasParseError())

        doc.Parse("{}");

}

32. rapidjson的“坑”

使用不當,則會掉進“坑”裡。下列程式碼在valgrind中執行時,會報大量錯誤,而且如果sub是在一個迴圈中被AddMember,則無法得到預期的結果。

從現象看像是sub析構後仍在被使用,為驗證這個推測,改成:rapidjson::Document* sub = new rapidjson::Document;,然後再使用不但valgrind不報錯,而且迴圈使用也沒問題,那麼可以肯定AddMember是淺拷貝,這樣一來使用就不方便了,除非還有深拷貝的呼叫方式。

 

#include <rapidjson/schema.h>

#include <rapidjson/document.h>

#include <rapidjson/stringbuffer.h>

#include <rapidjson/writer.h>

#include <string>

#include <vector>

 

int main()

{

    rapidjson::Document doc;

    doc.Parse("{}");

 

    { // 目的是讓sub在printf時已無效

        rapidjson::Document sub;

        sub.Parse("{\"name\":\"tom\"}");

        doc.AddMember("sub", sub, doc.GetAllocator());

    }

 

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

    doc.Accept(writer);

    printf("%s\n", buffer.GetString());

    return 0;

}

 

上述程式碼在valgrind中跑,會報錯大量如下這樣的錯誤:

==30425== Invalid read of size 2

==30425==    at 0x804B008: rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >::IsString() const (document.h:947)

==30425==    by 0x8051632: bool rapidjson::GenericValue<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >::Accept<rapidjson::Writer<rapidjson::GenericStringBuffer<rapidjson::UTF8<char>, rapidjson::CrtAllocator>, rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator, 0u> >(rapidjson::Writer<rapidjson::GenericStringBuffer<rapidjson::UTF8<char>, rapidjson::CrtAllocator>, rapidjson::UTF8<char>, rapidjson::UTF8<char>, rapidjson::CrtAllocator, 0u>&) const (document.h:1769)

==30425==    by 0x80488CE: main (f.cpp:30)

==30425==  Address 0x428eb62 is 58 bytes inside a block of size 65,548 free'd

==30425==    at 0x4023329: free (vg_replace_malloc.c:473)

==30425==    by 0x804BC72: rapidjson::CrtAllocator::Free(void*) (allocators.h:79)

==30425==    by 0x804BDD7: rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>::Clear() (allocators.h:148)

==30425==    by 0x804BE2E: rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>::~MemoryPoolAllocator() (allocators.h:140)

==30425==    by 0x804BE5F: rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::Destroy() (document.h:2382)

==30425==    by 0x804BE7E: rapidjson::GenericDocument<rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>, rapidjson::CrtAllocator>::~GenericDocument() (document.h:2064)

 

正確可以使用的寫法:

#include <rapidjson/schema.h>

#include <rapidjson/document.h>

#include <rapidjson/stringbuffer.h>

#include <rapidjson/writer.h>

#include <string>

#include <vector>

 

int main()

{

    std::vector<rapidjson::Document*> subs;

    rapidjson::Document doc;

    doc.Parse("{}");

 

    {    

        // 注意,下面沒有使用Document的預設構造,

        // 而是指定Allocator為其父的Allocator。

        // 如果存在多級Document,一定要統一使用根Document的Allocator,

        // 原因是Allocator分配的記憶體會隨Document析構被釋放掉!

        //

        // 如果不這樣做,必須保證sub的生命在doc之後才結束。

        rapidjson::Document sub(&doc.GetAllocator());

        sub.Parse("{\"name\":\"tom\"}");

        doc.AddMember("sub", sub, doc.GetAllocator());

    }

 

    rapidjson::StringBuffer buffer;

    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

    doc.Accept(writer);

    printf("%s\n", buffer.GetString());

 

    for (std::vector<rapidjson::Document*>::size_type i=0; i<subs.size(); ++i)

    {

        rapidjson::Document *sub_ptr = subs[i];

        delete sub_ptr;

    }

    subs.clear();

 

    return 0;

}

 

相關文章