I am trying to to create a class/struct array out of a json file with rapidjson. The file contains the following content:
{
"item" : [
{"name": "chair",
"attribute": "iron",
"available": "false"},
{"Name": "bed",
"attribute": "wood",
"available": "true",
"attribute":"soft"},
{"naeM": "lamp",
"attribute": "iron",
"available": "false",
"Number": "4"},
....
{"name": "mirrow",
"attribute": "iron",
"available": "false"}
],
"category" : [
{"name": "kitchen"}, {"name": "living room"},{"name": "bedroom"} ]
}
I had looked up all the info I could get on rapidjson.org and the examples but don't really get how to check for a specific key in each array object of validation if the key exist(case sensitive) and no duplicates in the object.
For example, here I want to check if each array object has the key "name" in it or if only the keys "name", "attribute" and "available" are present. The closed to get there would probably be to create a json schema valid check but I don't really get the syntax of it.
Here is a simple program that checks the whether each item
has those three attributes:
#define RAPIDJSON_HAS_STDSTRING 1
#include "rapidjson/document.h"
#include "rapidjson/istreamwrapper.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <iterator>
using namespace rapidjson;
using namespace std;
bool check_or_error(bool check, string message) {
if (!check) {
cerr << message << endl;
}
return check;
}
int main() {
Document doc;
{
// This just loads JSON from a file into `doc`.
ifstream fs("test2.json");
IStreamWrapper isw(fs);
doc.ParseStream(isw);
if (doc.HasParseError()) {
cerr << "Parse error " << doc.GetParseError() << " at " << doc.GetErrorOffset();
exit(1);
}
}
// Grab the element under `item` and coerce it to an array.
const Value::Array& a = doc["item"].GetArray();
// This is a standard iterator pattern; we set `it` equal to every element
// in turn. We use `cbegin`/`cend` to get const iterators, which do not
// modify the array.
for (auto it = cbegin(a); it != cend(a); it++) {
bool ok = true;
// Is it an object? if not, complain and move to the next element.
ok = check_or_error(it->IsObject(), "Item is not an object");
if (!ok) continue;
const Value::Object& item = it->GetObject();
int idx = std::distance(cbegin(a), it);
static std::string mandatory[] = { "name", "attribute", "available" };
// For every mandatory key ...
for (const auto& key : mandatory) {
// ... check if it is present or complain
// The weird `(stringstream() << ...).str()` construct
// allows us to creata formatted message as a string.
ok = check_or_error(item.HasMember(key), (stringstream() << "item[" << idx << "] missing key " << key).str());
if (!ok) continue;
// ... check if it is a string or complain
const Value& v = item[key];
check_or_error(v.IsString(), (stringstream() << "item[" << idx << "][" << key << " not a string ").str());
}
}
}
Note that it does not check for duplicate keys, as RapidJSON hides that for you.
The corresponding JSONSchema would be: (test for yourself)
{
"type": "object",
"required": [
"item",
"category"
],
"properties": {
"item": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"attribute",
"available"
],
"properties": {
"name": {
"type": "string"
},
"attribute": {
"type": "string"
},
"available": {
"type": "string"
}
}
}
},
"category": {
"type": "array",
"items": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
}
}
}
}
}
}