boost::ptree find? or how to access deep arrays? C++ - json

I have been trying for too much time now, to access a json_reader ptree from the boost library.
I have a json file that is capsulated really often: (pseudo-json:)
"Foo": {
"nameofFoo:"foofoo"
"Bar": [{
"BarFoo":
{ BarFooDeep: {
BarFooDeepDeep: {
"BarFooValue1": 123
"BarFooValue2" : 456
}
}
}
"FooBar": [ {
"FooBarDeep" :[ {
FooBarDeepDeep:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
FooBarDeepDeep1:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
"FooBarDeep" :[ {
FooBarDeepDeep2:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
FooBarDeepDeep3:[ {
FooBarValue1: "ineedthis"
FooBarValue2: "andthis"
} ]
and so on .... won t complete this now...
Now I only need to get FooBarValue1 and FooBarValue2 of all FooBar.
I know ptree puts arrays together with empty childs ("")
I can access all the members by itereration over all the childs recursively.
But is there not a better way to access special values?
how does ptree find works? i always get compiler errors ...
ptree jsonPT;
read_json( JSON_PATH, jsonPT);
ptree::const_iterator myIT = jsonPT.find("FooBarValue1");
double mlat = boost::lexical_cast<int>(myIT->second.data());
error: conversion from
‘boost::property_tree::basic_ptree,
std::basic_string >::assoc_iterator’ to non-scalar type
‘boost::property_tree::basic_ptree,
std::basic_string >::const_iterator’ requested
ptree::const_iterator myIT = jsonPT.find("FooBarValue1");
Can anyone give me a useful hint how to get access to this ptree?!?

As hinted in the linked answer I commented (Boost.PropertyTree subpath processing), you could write your own "selector" query, so you could write stuff like:
read_json("input.txt", pt);
std::ostream_iterator<std::string> out(std::cout, ", ");
std::cout << "\nSpecific children but in arrays: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..FooBarDeepDeep6..FooBarValue2", out);
std::cout << "\nSingle wildcard: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..*..FooBarValue2", out);
std::cout << "\nTwo wildcards: ";
enumerate_path(pt, "Foo.Bar..FooBar..*..*..FooBarValue2", out);
The enumerate_path function need not be too complicated and takes any output iterator (so you can back_inserter(some_vector) just as well):
template <typename Tree, typename Out, typename T = std::string>
Out enumerate_path(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
*out++ = pt.template get<T>(path);
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_path(child.second, path, out);
}
}
}
return out;
}
As simple working demo prints:
Specific children but in arrays: andthis6,
Single wildcard: andthis6, andthis7, andthis8, andthis9,
Two wildcards: andthis1, andthis2, andthis3, andthis4, andthis6, andthis7, andthis8, andthis9,
That is with the following input.txt:
{
"Foo": {
"nameofFoo": "foofoo",
"Bar": [{
"BarFoo": {
"BarFooDeep": {
"BarFooDeepDeep": {
"BarFooValue1": 123,
"BarFooValue2": 456
}
}
},
"FooBar": [{
"FooBarDeep0": [{
"FooBarDeepDeep1": [{
"FooBarValue1": "ineedthis1",
"FooBarValue2": "andthis1"
}],
"FooBarDeepDeep2": [{
"FooBarValue1": "ineedthis2",
"FooBarValue2": "andthis2"
}]
},
{
"FooBarDeepDeep3": [{
"FooBarValue1": "ineedthis3",
"FooBarValue2": "andthis3"
}],
"FooBarDeepDeep4": [{
"FooBarValue1": "ineedthis4",
"FooBarValue2": "andthis4"
}]
}],
"FooBarDeep1": [{
"FooBarDeepDeep6": [{
"FooBarValue1": "ineedthis6",
"FooBarValue2": "andthis6"
}],
"FooBarDeepDeep7": [{
"FooBarValue1": "ineedthis7",
"FooBarValue2": "andthis7"
}]
},
{
"FooBarDeepDeep8": [{
"FooBarValue1": "ineedthis8",
"FooBarValue2": "andthis8"
}],
"FooBarDeepDeep9": [{
"FooBarValue1": "ineedthis9",
"FooBarValue2": "andthis9"
}]
}]
}]
}]
}
}
Live On Coliru
Full Listing
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
template <typename Tree, typename Out, typename T = std::string>
Out enumerate_path(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
*out++ = pt.template get<T>(path);
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_path(child.second, path, out);
}
}
}
return out;
}
int main() {
std::ostream_iterator<std::string> out(std::cout, ", ");
using namespace boost::property_tree;
ptree pt;
read_json("input.txt", pt);
std::cout << "\nSpecific children but in arrays: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..FooBarDeepDeep6..FooBarValue2", out);
std::cout << "\nSingle wildcard: ";
enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..*..FooBarValue2", out);
std::cout << "\nTwo wildcards: ";
enumerate_path(pt, "Foo.Bar..FooBar..*..*..FooBarValue2", out);
}

find() is for retrieving a child node by key; it doesn't search the whole ptree. It returns an assoc_iterator (or const_assoc_iterator), which you can convert to an iterator via the to_iterator() method on the parent:
ptree::const_assoc_iterator assoc = jsonPT.find("FooBarValue1");
ptree::const_iterator myIT = jsonPT.to_iterator(assoc);
To search the ptree, you'll need to iterate it recursively:
struct Searcher {
struct Path { std::string const& key; Path const* prev; };
void operator()(ptree const& node, Path const* path = nullptr) const {
auto it = node.find("FooBarValue1");
if (it == node.not_found()) {
for (auto const& child : node) { // depth-first search
Path next{child.first, path};
(*this)(child.second, &next);
}
} else { // found "FooBarValue1"
double mlat = boost::lexical_cast<int>(myIT->second.data());
// ...
std::cout << "Mlat: " << mlat << std::endl;
std::cout << "Path (reversed): ";
for (Path const* p = path; p != nullptr; p = p->prev)
std::cout << p << ".";
std::cout << std::endl;
}
}
};
Searcher{}(jsonPT);
Alternatives for writing the recursive traversal would be a C++14 generic lambda, or in C++11 a type-erased concrete lambda using std::function.

Related

How to write multiple ssid's and passwords to the same json file ESP32

I am currently testing a piece of code using arduinojson6. My goal is to store multiple ssid's and passwords to the esp32 SPIFF.
The uneddited question contained a piece of code that would append to the file rather than reading doc, deleting /SSID.json, adding to doc serialization and saving the file again like I have now, is also not the solution.
the desired json file would be:
{"information":[{ "SSID":"variable blaat1", "PASS1":"variable Abc1", "NUMBER": "1" },{ "SSID":"variable blaat2", "PASS2":"variable Abc2", "NUMBER": "2" },{ "SSID":"variable blaat3", "PASS3":"variable Abc3", "NUMBER": "3" },{ "SSID":"variable blaat4", "PASS4":"variable Abc4", "NUMBER": "4" },{ "SSID":"variable blaat5", "PASS5":"variable Abc5", "NUMBER": "5" }]}
Instead, when more then 1 value is serialized and appended it will read like this:
{
"information": {},
"test": [
"mooiman\n",
"mooiweer\n"
],
"number": [
1,
2
]
}
Maybe some of you know how to serialize it properly.
The code I test with:
#include <Arduino.h>
#include <WiFi.h>
//#include <time.h>
//#include <ESP32Ping.h>
#include "FS.h"
#include "SPIFFS.h"
//#include <HTTPClient.h>
#include <ArduinoJson.h>
int numberofInputs = 1;
String ssid = "YourSSID";
String passwords = "YourPassword";
String readString;
char FileReadBuff[1024];
DynamicJsonDocument doc(1024);
void readFile(fs::FS &fs, const char * path){
if (SPIFFS.exists("/SSID.json") == false)
{
File file = SPIFFS.open("/SSID.json", FILE_WRITE);
if (!file) {
Serial.println("There was an error opening the file for writing");
return;
}
if (file.print("SSID")) {
Serial.println("File was written");
} else {
Serial.println("File write failed");
}
file.close();
}
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return;
}
uint16_t i = 0;
Serial.println("reading");
while (file.available()) {
FileReadBuff[i] = file.read();
i++;
}
file.close();
}
void CleanFile(fs::FS &fs, const char * path, const char * message) {
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
File file = SPIFFS.open(path, FILE_WRITE);
if (fs.remove(path)) {
Serial.println("\r\n- file cleaned");
} else {
Serial.println("\r\n- Cleaning failed");
}
file.print(path);
}
void appendFile(fs::FS &fs, const char * path, const char * message){
if (SPIFFS.exists("/SSID.json") == false)
{
File file = SPIFFS.open("/SSID.json", FILE_WRITE);
if (!file) {
Serial.println("There was an error opening the file for writing");
return;
}
if (file.print("SSID")) {
Serial.println("File was written");
} else {
Serial.println("File write failed");
}
file.close();
}
Serial.printf("Appending to file: %s\r\n", path);
File file = fs.open(path, FILE_APPEND);
if(!file){
Serial.println("- failed to open file for appending");
return;
}
if(file.println(message)){
Serial.println("- message appended");
} else {
Serial.println("- append failed");
}
file.close();
}
void Deserialization(){
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
readFile(SPIFFS, "/SSID.json"); //read everything from ssid.json file
const char * JsonFF = FileReadBuff; // put everything in to const char
Serial.print("Json From File:"); Serial.println(JsonFF);
DeserializationError error = deserializeJson(doc, JsonFF);
if(error){
Serial.print(F("deserializeJson() failed: ")); Serial.println(error.f_str()); // if not legit print error
}
if(!error){
String information = doc["information"];
Serial.println(information);
information = "";
}
}
void testjson(){
readString = "";
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
if (readString.indexOf("READ") >= 0) {
Serial.println("reading file");
readFile(SPIFFS, "/SSID.json");
Serial.println(FileReadBuff);
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
}
if (readString.indexOf("DES") >= 0) { //DEZ deserialize will result in an error because json file is currently not valid
Serial.println("reading deserialized json");
Deserialization();
}
if (readString.indexOf("CLEAN") >= 0) { //CLEAN cleans the SSID.json file
Serial.println("reading deserialized json");
CleanFile(SPIFFS, "/SSID.json", "");
}
if (readString.indexOf("WRANDOM") >= 0){ //WRANDOM writes a random string to the SSID.json file
readString = "";
Serial.println("Going to write the following input:"); //waiting for user input
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
`here is the part we're talking about`
CleanFile(SPIFFS, "/SSID.json", "");
JsonObject information = doc.createNestedObject("information");
String SerializedJson = "";
doc["test"].add(readString);
doc["number"].add(numberofInputs);
serializeJsonPretty(doc, SerializedJson);
appendFile(SPIFFS, "/SSID.json", SerializedJson.c_str());
SerializedJson = "";
numberofInputs ++;
return;
}
}
}
}
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
if (SPIFFS.exists("/SSID.json") == false)
{
File file = SPIFFS.open("/SSID.json", FILE_WRITE);
if (!file) {
Serial.println("There was an error opening the file for writing");
return;
}
if (file.print("SSID")) {
Serial.println("File was written");
} else {
Serial.println("File write failed");
}
file.close();
}
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(ssid.c_str(), passwords.c_str());
while (WiFi.status() != WL_CONNECTED) { //Check for the connection
delay(1000);
Serial.print(".");
}
Serial.println("Connected");
}
void loop() {
// put your main code here, to run repeatedly:
readString = ""; //clears variable for new input
Serial.println("Ready for new input: ");
testjson();
}
So when you serial write WRANDOM you'll get promted to put in something.
When that is recieved it will store that in json.
Do that another time.
Next when you serial write READ it will show you the saved /SSID.json.
Thanks in advance.
Note that the DynamicJsonDocument startsof empty.
PS. I know littlefs is the new spiff but lets first try to make this work (or I need to make sepperate files for every ssid+password)
Here is a test sketch you can implement to your project.
I did not had the time to write more detailed sketch than this.
Let me know if you have any question about this
#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>
#include <ArduinoJson.h>
#define DOC_SIZE 5000
/*
Example JSON :
{
"information":[
{ "SSID":"variable blaat1", "PASS1":"variable Abc1", "NUMBER": "1" },
{ "SSID":"variable blaat2", "PASS2":"variable Abc2", "NUMBER": "2" },
{ "SSID":"variable blaat3", "PASS3":"variable Abc3", "NUMBER": "3" },
{ "SSID":"variable blaat4", "PASS4":"variable Abc4", "NUMBER": "4" },
{ "SSID":"variable blaat5", "PASS5":"variable Abc5", "NUMBER": "5" }
]
}
*/
/*
When you create a JSON array, you don't need to push the index of the object, since you can later
ask for the array size, therefor you know the indexes. Also you don't need to separate the passwords
like PASS1 or PASS2 since every cred is a separate object.
*/
void addCredentials( const char* SSID, const char* PW ){
// Create a static JSON document.
StaticJsonDocument fileDoc(DOC_SIZE);
// Open the file containing the credentials
File credFile = LittleFS.open(FILE_PATH,FILE_READ);
// Check if it opened.
if( !credFile ){ Serial.println("Failed to open file"); return; }
// Deserialize the file
DeserializationError error = deserializeJson(fileDoc, credFile );
// Check if the deserialization has any errors.
if( error ){ Serial.printf("Error on deserialization: %s\n", error.c_str() );
// Get the info array from the JSON.
JsonArray infoArr = fileDoc["information"].as<JsonArray>();
// Create a new object inside the array.
JsonObject newCred = infoArr.createNestedObject();
// Add credentials to the object.
newCred["SSID"] = SSID;
newCred["PASS"] = PW;
// Serialize everythig back to the file.
serializeJson(fileDoc, credFile);
// Close the file.
credFile.close();
}
// Open the file and put the pretty json into the serial directly.
void printFileContent(){
File credFile = LittleFS.open(FILE_PATH,FILE_READ);
if( !credFile ){ Serial.println("Failed to open file"); return; }
serializeJsonPretty(Serial, credFile);
credFile.close();
}
void clearFile(){
if( !LittleFS.exists(FILE_PATH) ){ Serial.println("Credentials file does not exists yet!"); return; }
File credFile = LittleFS.open(FILE_PATH,FILE_READ);
credFile.remove();
}
void getUserInput(){
if( !Serial.available() ){ return; }
// Get the credentials or whatever from the serial
// and call the **addCredentials( const char* SSID, const char* PW );** function.
}
void setup() {
Serial.begin(115200);
}
void loop() {
getUserInput();
}
I got it figured out with help from Dr.Random. It wasn't the full answer but it helped getting the json format and apend to that file correctly. The following code is a working example.
#include <Arduino.h>
#include <WiFi.h>
#include "FS.h"
#include "LittleFS.h"
#include <ArduinoJson.h>
#define FORMAT_LITTLEFS_IF_FAILED true
#define DOC_SIZE 5000
#define FILE_PATH "/SSID.json"
String ssid = "yourssid";
String passwords = "yourpass";
String readString;
char FileReadBuff[1024];
void readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return;
}
Serial.println("- read from file:");
while(file.available()){
Serial.write(file.read());
}
file.close();
}
void Showone(int wichone){
DynamicJsonDocument doc(DOC_SIZE);
File credFile = LittleFS.open(FILE_PATH, FILE_READ);// open file for reading
DeserializationError error = deserializeJson(doc, credFile);// deserialize json
if( !credFile ){ Serial.println("Failed to open file"); return; }// if file doesn't exist, return
const char* SSID = doc[wichone]["SSID"]; //
const char* PASS = doc[wichone]["PASS"]; //
Serial.print("SSID and PASS = "); Serial.print(SSID); Serial.print(PASS); Serial.print(" "); Serial.println(" ");
credFile.close();
}
void addCredentials(const char * input){
DynamicJsonDocument doc(DOC_SIZE);// create json doc
String Serialized; // create string to store serialized json
File credFile = LittleFS.open(FILE_PATH, FILE_READ);// open file for reading
if( !credFile ){ Serial.println("Failed to open file"); return; }// if file doesn't exist, return
DeserializationError error = deserializeJson(doc, credFile);// deserialize json
if( error ){ Serial.printf("Error on deserialization: %s\n", error.c_str() );} //error when spiff is empty or not formatted correctly
JsonArray inforArr = doc["information"].as<JsonArray>();// get array from json
JsonObject newCred = doc.createNestedObject();// create new object in json
newCred["SSID"] = input;
newCred["PASS"] = input;
serializeJsonPretty(doc, Serialized);
Serial.print("input = "); Serial.println(input);
Serial.print("Serialized: "); Serial.println(Serialized);
File credFile2 = LittleFS.open(FILE_PATH, FILE_WRITE);// open file for writing
credFile2.print(Serialized);
credFile2.close();
credFile.close();
Serialized = "";
}
void testjson(){
readString = "";
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
if (readString.indexOf("READ") >= 0) {
Serial.println("reading file");
readFile(LittleFS, "/SSID.json");
Serial.println(FileReadBuff);
for( int i = 0; i < sizeof(FileReadBuff); ++i ){
FileReadBuff[i] = (char)0;
}
}
if (readString.indexOf("SHOW") >= 0) {
readString = "";
Serial.println("Showing the following input(number): "); //waiting for user input
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
Showone(readString.toInt());
}
}
}
if (readString.indexOf("CLEAN") >= 0) { //CLEAN cleans the SSID.json file
Serial.println("reading deserialized json");
File credFile = LittleFS.open(FILE_PATH, FILE_WRITE);// open file for writing
credFile.print("");
credFile.close();
}
if (readString.indexOf("WRANDOM") >= 0){ //WRANDOM writes a random string to the SSID.json file
readString = "";
Serial.println("Going to write the following input:"); //waiting for user input
while(readString.length() < 1) {
while (Serial.available()) {
delay(10); //small delay to allow input buffer to fill
if (Serial.available() > 0) {
char c = Serial.read(); //gets one byte from serial buffer
if (c == ',') {
break;
} //breaks out of capture loop to print readstring
readString += c;
} //makes the string readString
}
if (readString.length() > 0) {
Serial.println(readString); //prints string to serial port out
addCredentials(readString.c_str());
}
}
}
}
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
Serial.println("LittleFS Mount Failed");
return;
}
//if /ssid.json doesn't exist, create it littlefs
if(!LittleFS.exists(FILE_PATH)){
Serial.println("File doesn't exist, creating it");
File credFile = LittleFS.open(FILE_PATH, FILE_WRITE);
if(!credFile){
Serial.println("Failed to create file");
return;
}
credFile.close();
}
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(ssid.c_str(), passwords.c_str());
while (WiFi.status() != WL_CONNECTED) { //Check for the connection
delay(1000);
Serial.print(".");
}
Serial.println("Connected");
}
void loop() {
// put your main code here, to run repeatedly:
readString = ""; //clears variable for new input
Serial.println("Ready for new input: ");
testjson();
}
Any improvements are still desirable.

boost::property_tree::ptree accessing array's first complex element

My JSON is this:
{
"apps":[
{
"id":"x",
"val":"y",
}
]
}
I can get id's value "x" by looping, and exiting when it.first is id:
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
What I want, however, is to get the id's value by something like this:
std::cout << root.get<std::string>("apps[0].id").c_str() << std::endl;
Of course this displays nothing to me, for I am probably using wrong syntax accessing 1st element of the apps array. It might be that this has to be done in a different way all together.
I have found only this ugly and dangerous method:
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
I cannot really use it this way as it won't throw an exception when the array is empty, it will core dump!
Below is the whole program to make it easier for any one who wants to help:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
int main()
{
std::stringstream ss("{\"apps\":[{\"id\":\"x\",\"val\":\"y\"}]}");
ptree root;
read_json(ss, root);
for (const ptree::value_type &app : root.get_child("apps"))
{
for (const ptree::value_type &it : app.second) {
if (it.first == "id") {
std::cout << it.second.get_value<std::string>().c_str() << std::endl;
}
}
}
std::cout << root.get_child("apps").begin()->second.begin()->second.get_value<std::string>().c_str() << std::endl;
return 0;
}
As the docs say, array elements are nodes with "" keys.
If you're after the first element, you're in luck:
root.get("apps..id", "")
The .. in the path selects the first empty key
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({"apps":[{"id":"x","val":"y"}]})");
ptree root;
read_json(ss, root);
std::cout << root.get("apps..id", "") << "\n";
}
BONUS
If you need to address elements other than the first, write a helper function. This would be a good start:
#include <string>
#include <stdexcept> // std::out_of_range
template <typename Tree>
Tree query(Tree& pt, typename Tree::path_type path) {
if (path.empty())
return pt;
auto const head = path.reduce();
auto subscript = head.find('[');
auto name = head.substr(0, subscript);
auto index = std::string::npos != subscript && head.back() == ']'
? std::stoul(head.substr(subscript+1))
: 0u;
auto matches = pt.equal_range(name);
if (matches.first==matches.second)
throw std::out_of_range("name:" + name);
for (; matches.first != matches.second && index; --index)
++matches.first;
if (index || matches.first==matches.second)
throw std::out_of_range("index:" + head);
return query(matches.first->second, path);
}
Here's some live tests using it:
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::stringstream ss(R"({
"apps": [
{
"id": "x",
"val": "y",
"id": "hidden duplicate"
},
{
"id": "a",
"val": "b"
}
]
})");
ptree root;
read_json(ss, root);
for (auto path : {
"apps..id", "apps.[0].id", // (equivalent)
//
"apps.[0].id[]", // invalid
"apps.[0].id[0]", // x
"apps.[0].id[1]", // hidden duplicate
"apps.[1].id", // a
"apps.[1].id[0]", // a
"apps.[1].id[1]", // out of range
"apps.[2].id", // out of range
"drinks" // huh, no drinks at the foo bar
}) try {
std::cout << "Path '" << path << "' -> ";
std::cout << query(root, path).get_value<std::string>() << "\n";
} catch(std::exception const& e) {
std::cout << "Error: " << e.what() << "\n";
}
}
Prints:
Path 'apps..id' -> x
Path 'apps.[0].id' -> x
Path 'apps.[0].id[]' -> Error: stoul
Path 'apps.[0].id[0]' -> x
Path 'apps.[0].id[1]' -> hidden duplicate
Path 'apps.[1].id' -> a
Path 'apps.[1].id[0]' -> a
Path 'apps.[1].id[1]' -> Error: index:id[1]
Path 'apps.[2].id' -> Error: index:[2]
Path 'drinks' -> Error: name:drinks

What does this Binary search tree function do?

I was wondering what does this function do and what could be the possible output of this code?
void TreeType::Function()
{
Queue<TreeNode*> q;
TreeNode* node;
if (root!= NULL) {
q.Enqueue(root);
do {
q.Dequeue(node);
cout << node->info << endl;
if (node->left)
{ q.Enqueue(node->left); }
if (node->right)
{ q.Enqueue(node->right); }
while (!q.IsEmpty()); }
this function is like level order traversal of any tree.
while there are many ending semi colons missing.

Why does my current_user pointer reset?

current_user pointer works within functions of System.cpp, but resets afterwards. The current_user seems to be updated locally when it's set within the function createUser(string name) when it's called in the switch statement. However, when I try to call it outside the function it seems to have not been updated at all. Not entirely sure what's going on.
Main.cpp
#include <iostream>
#include "System.h"
#include "User.h"
using namespace std;
int main()
{
System s;
s.run();
// Works
User a("Test");
User b("Test 2");
User c("Test 3");
User* current = &a;
cout << "The current user is: " << current->getName() << endl;
current = &b;
cout << "Now it's: " << current->getName() << endl;
current = &c;
cout << "The final current user is: " << current->getName() << endl;
// Does not work
cout << "Current user: " << s.getCurrentUser()->getName() << endl;
}
System.h
#ifndef SYSTEM_H
#define SYSTEM_H
#include "User.h"
#include "Group.h"
#include "MessageBuffer.h"
#include "Banner.h"
#include <iostream>
#include <vector>
class System
{
public:
System();
char validInput(std::string inputIn);
bool validUsername(std::string nameIn);
bool userExists(std::string nameIn);
void createUser(std::string nameIn);
void run();
User* getCurrentUser();
private:
User* current_user;
std::vector<User> user_list;
std::vector<Group> group_list;
};
#endif // SYSTEM_H
System.cpp
// Program 1: TigerBook Social Network
// File: System.cpp
// Description: Class Implimentation of the System Class. Instantiates objects that must be initialized and handles
// basic user screw-ups (choosing and option out of bounds).
#include <iostream>
#include "System.h"
using namespace std;
// Function: Default System Constructor
// Inputs: None
// Description: Default constructor for the class.
System::System()
{
}
User* System::getCurrentUser()
{
return current_user;
}
// Function: validInput
// Inputs: string inputIn
// Outputs: char value of input at 0
// Description: Determines whether the input is valid.
char System::validInput(string inputIn)
{
if (inputIn.length() == 1)
{
return inputIn[0];
}
else
{
return '0';
}
}
// Function: validUsername
// Inputs: string username
// Outputs: true if valid, false if not
// Description: Determines whether the username is valid
bool System::validUsername(string nameIn)
{
if (nameIn.empty() || nameIn.length() < 2 || (nameIn.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") != string::npos))
{
cerr << "\n\t*** ERROR: Invalid user name, please try again! ***" << endl;
return false;
}
else
{
return true;
}
}
// Function: userExists
// Inputs: string username
// Outputs: true if exists, false if not
// Description: Determines whether the username exists in user_list.
bool System::userExists(string nameIn)
{
return false;
}
// Function: createUser
// Inputs: string username
// Outputs: void
// Description: Creates new user and adds it to user_list.
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &u;
}
// Function: run
// Inputs: None
// Outputs: void
// Description: Program driver, handles basic user input and screw-ups
void System::run()
{
//current_user = NULL;
Banner banner("The TigerBook Social Network!");
cout << banner.getBanner() << endl;
bool quit = false;
string input;
while(!quit)
{
cout << "\nCreate new user (n), Broadcast (b), Multicast (m), Unicast (u), Wall page (w), Home page (h), Create new group (g), " << endl;
cout << "Join a group (j), Switch user (s), Quit (q)\n" << endl;
cout << "Choose an option: ";
getline(cin, input);
if (current_user == NULL && (input != "N" && input != "n" && input != "Q" && input != "q"))
{
cerr << "\n\t*** ERROR: There is no current user, please create a new user! ***" << endl;
continue;
}
switch (validInput(input))
{
// Create new user
case 'N':
case 'n':
{
string username;
cout << "\nPlease enter user name: ";
getline(cin, username);
if (!validUsername(username))
{
continue;
}
else if (userExists(username))
{
cerr << "\n\t*** ERROR: The user \"" + username + "\" already exists, please try again! ***" << endl;
continue;
}
else
{
createUser(username);
cout << "\nCurrent user: " << getCurrentUser()->getName() << endl; // test current_user
}
break;
}
case 'B':
case 'b':
{
break;
}
case 'M':
case 'm':
{
break;
}
case 'U':
case 'u':
{
break;
}
case 'W':
case 'w':
{
break;
}
case 'H':
case 'h':
{
break;
}
case 'G':
case 'g':
{
break;
}
case 'J':
case 'j':
{
break;
}
case 'S':
case 's':
{
break;
}
case 'Q':
case 'q':
{
quit = true;
banner.setBanner("Thank you for using TigerBook Social Network!");
cout << banner.getBanner() << endl << endl;
break;
}
default:
{
cerr << "\n\t*** ERROR: Invalid input, please try again! ***" << endl;
}
} // End of switch statement
} // End of loop
}
User.h
#ifndef USER_H
#define USER_H
#include <string>
class User
{
public:
User();
User(std::string nameIn);
std::string getName();
void setName(std::string nameIn);
private:
std::string name;
};
#endif // USER_H
User.cpp
// Program 1: TigerBook Social Network
// File: User.cpp
// Description: Class implementation for User class
#include "User.h"
using namespace std;
// Constructor (Default)
User::User()
{
//ctor
}
// Constructor
// Inputs: string that sets name
// Description: Constructs user object and assigns its name.
User::User(string nameIn)
{
name = nameIn;
}
// Function: setName
// Inputs: string that sets name
// Outputs: void
// Description: Sets name of user object.
void User::setName(string nameIn)
{
name = nameIn;
}
// Function: getName
// Inputs: none
// Outputs: Name of user
// Description: Returns the name of the user object.
string User::getName()
{
return name;
}
You have:
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &u;
}
Here, you are storing pointer to a local variable. The pointer becomes a dangling pointer as soon as the function returns. Any use of current_usr to access the object after the function returns is cause for undefined behavior.
You can use:
void System::createUser(string nameIn)
{
User u(nameIn);
user_list.push_back(u);
current_user = &(user_list.back());
}
Even that is very fragile. It will work if you carefully manage the objects in user_list.
A better option will be not to store a pointer to the object at all. You can, for example, use:
User* System::getCurrentUser()
{
if ( user_list.empty() )
{
return nullptr;
}
return &(user_list.back());
}

json parsing gives error

{
"Restricted_parameters":
{
"abcd"
"efgh"
"ijkl"
"mnop"
}
}
I am new about json files and parsing it and in my current college project when I am rying to parse the json file it is giving error
Can anyone please let me know how to parse above json file
I am using JSON parser APIs also
Jason_parser_edf::Jason_parser_edf()
{
Json_parser file_parser;
// Create empty property tree object
using boost::property_tree::ptree;
ptree pt;
uint32_t nb = 0;
std::string // const std::string restricted_parameters = "Restricted_parameters";
file_parser.open_json_file(current_file_path, &pt);
ptree::const_iterator end = pt.end();
for (ptree::const_iterator it = pt.begin(); it != end; ++it)
{
BOOST_FOREACH( ptree::value_type const& v, pt.get_child(it->first) ) // parasoft-suppress MISRA2008-6_4_1 "BOOST library" // parasoft-suppress NAMING-33 "BOOST library" // parasoft-suppress BD-PB-CC "BOOST library" // parasoft-suppress MISRA2008-6_3_1 "BOOST library"
{
// Getting additional fields
std::string additional_field_name = v.second.get<std::string>("Restricted_parameters");
cout << additional_field_name << endl;
}
}
// second way I am trying
for (auto & array_element: pt) {
for (auto & property: array_element.second) {
std::cout << property.first << endl;
}
}
// db_conf_structure.dump();
}
This is not a valid format of JSON, you must remember always to have pair key: value
{
"Restricted_parameters":
{
"abcd": "val1",
"efgh": "val2",
"ijkl": "val3",
"mnop": "val4"
}
}
or use array
{
"Restricted_parameters":
[
"abcd",
"efgh",
"ijkl",
"mnop"
]
}
The JSON is invalid. An array is initialized through square brackets and you need commas between the items.
{
"Restricted_parameters":
[
"abcd",
"efgh",
"ijkl",
"mnop"
]
}