I came across How to convert XML to JSON in ASP.NET C# (link) and one in javascript (at goessner.net/download/prj/jsonxml/). But have yet to find one in c++ that takes just a string as input (Or a tinyxml node as I'm using that library).
By far, the only specifically designed C++ library that directly converts XML to JSON I found on the Internet is xml2json: https://github.com/Cheedoong/xml2json
Please head to http://json.org. You may find something of interest there. It lists many libraries available for JSON processing in C++ and many other languages.
But for the thing you linked to [pdhcc] you may have to do some work yourself to port it to C++.
Ideally this would be done using XSLT instead writing a translator in C++/C#. You can find some solutions online (for example this one).
However, if you would prefer to do it in C++ then I recommend the jsoncpp library.
Have you looked at http://www.json.org/?
I implemented this function to convert xml to json using jsoncpp & rapidxml libraries, maybe will help someone.
To compile : g++ -o xmltojson main.cpp -ljsoncpp.
Tested on debian 9 only.
#include <set>
#include <string>
#include <string.h>
#include <iostream>
#include <jsoncpp/json/json.h>
#include <rapidxml/rapidxml.hpp>
void parse_node_tree(const rapidxml::xml_node<>* node, Json::Value& parent)
{
Json::Value obj(Json::objectValue);
std::multiset<std::string> array;
for(rapidxml::xml_node<> *child = node->first_node(); child != nullptr; child = child->next_sibling())
{
if(child->type() != rapidxml::node_element)
continue;
array.insert(child->name());
}
if(node->value_size() > 0)
{
obj["#text"] = node->value();
}
bool hasChilds = false;
for(rapidxml::xml_attribute<> *attr = node->first_attribute(); attr != nullptr; attr = attr->next_attribute())
{
hasChilds = true;
obj[attr->name()] = attr->value();
}
for(rapidxml::xml_node<> *child = node->first_node(); child != nullptr; child = child->next_sibling())
{
if(child->type() != rapidxml::node_element)
continue;
hasChilds = true;
Json::Value& next = obj[child->name()];
if(array.count(child->name()) > 1 && !next.isArray())
{
next = Json::arrayValue;
}
parse_node_tree(child, next);
}
// set result.
if(parent.isArray())
{
parent.append(obj);
}
else
{
if(obj.isArray() || hasChilds)
parent = obj;
else
parent = node->value();
}
}
// convert xml string to json
std::string xmltojson(const std::string& xml)
{
char xml_text[xml.size()+1];
memset(xml_text, 0, xml.size());
strncpy(xml_text, xml.c_str(), xml.size());
xml_text[xml.size()] = '\0';
rapidxml::xml_document<> doc;
try
{
doc.parse<0>(xml_text);
}
catch(rapidxml::parse_error& exp)
{
std::cout << exp.what() << std::endl;
return std::string();
}
rapidxml::xml_node<> *node = doc.first_node();
if(node == nullptr)
return std::string();
Json::Value jdoc;
Json::Value& jroot = jdoc[node->name()];
parse_node_tree(node, jroot);
Json::FastWriter fast_writer;
return fast_writer.write(jdoc);
}
int main(int argc, char** argv)
{
// xml
std::string test_xml = "<anagrafica><testata><nomemercato id='007'>Mercato di test</nomemercato><data>Giovedi 18 dicembre 2003 16.05.29</data></testata><record><codice_cliente>5</codice_cliente><rag_soc>Miami American Cafe</rag_soc><codice_fiscale>IT07654930130</codice_fiscale><indirizzo tipo='casa'>Viale Carlo Espinasse 5, Como</indirizzo><num_prodotti>13</num_prodotti></record><record><codice_cliente>302</codice_cliente><rag_soc>Filiberto Gilardi</rag_soc><codice_fiscale>IT87654770157</codice_fiscale> <indirizzo tipo='ufficio'>Via Biancospini 20, Messina</indirizzo><num_prodotti>8</num_prodotti> </record><record><codice_cliente>1302</codice_cliente><rag_soc>Eidon</rag_soc><codice_fiscale>IT887511231</codice_fiscale><indirizzo tipo='ufficio'>Via Bassini 17/2, Milano</indirizzo><num_prodotti>18</num_prodotti> </record><record><codice_cliente>202</codice_cliente><rag_soc>SkillNet</rag_soc><codice_fiscale>IT887642131</codice_fiscale><indirizzo tipo='ufficio'>Via Chiasserini 11A, Milano</indirizzo><num_prodotti>24</num_prodotti></record><record><codice_cliente>12</codice_cliente><rag_soc>Eidon</rag_soc><codice_fiscale>IT04835710965</codice_fiscale><indirizzo tipo='casa'>Via Cignoli 17/2, Roma</indirizzo><num_prodotti>1112</num_prodotti></record><record><codice_cliente>5</codice_cliente><rag_soc>Miami American Cafe</rag_soc><codice_fiscale>IT07654930130</codice_fiscale><indirizzo tipo='casa'>Viale Carlo Espinasse 5, Como</indirizzo><num_prodotti>13</num_prodotti></record><record><codice_cliente>302</codice_cliente><rag_soc>Filiberto Gilardi</rag_soc><codice_fiscale>IT87654770157</codice_fiscale><indirizzo tipo='ufficio'>Via Biancospini 20, Messina</indirizzo><num_prodotti>8</num_prodotti></record><record><codice_cliente>1302</codice_cliente><rag_soc>Eidon</rag_soc><codice_fiscale>IT887511231</codice_fiscale><indirizzo tipo='ufficio'>Via Bassini 17/2, Milano</indirizzo><num_prodotti>18</num_prodotti></record><record><codice_cliente>202</codice_cliente><rag_soc>SkillNet</rag_soc><codice_fiscale>IT887642131</codice_fiscale><indirizzo tipo='ufficio'>Via Chiasserini 11A, Milano</indirizzo><num_prodotti>24</num_prodotti></record><record><codice_cliente>202</codice_cliente><rag_soc>SkillNet</rag_soc><codice_fiscale>IT887642131</codice_fiscale><indirizzo tipo='ufficio'>Via Chiasserini 11A, Milano</indirizzo><num_prodotti>24</num_prodotti></record><record><codice_cliente>12</codice_cliente><rag_soc>Eidon</rag_soc><codice_fiscale>IT04835710965</codice_fiscale><indirizzo tipo='casa'>Via Cignoli 17/2, Roma</indirizzo><num_prodotti>1112</num_prodotti></record></anagrafica>";
// convert
std::string json = xmltojson(test_xml);
// log
std::cout << test_xml << std::endl;
std::cout << std::endl;
std::cout << json << std::endl;
return 0;
}
Related
I've been trying to read this file for some time now and tried about everything I could think of. I placed the file in my Products folder and In my resource folder and included (ResourcePath + "File.cfg.txt") and neither worked. I'd appreciate it if someone could tell me what I'm missing and where to put this file to read it in. Again I'm using Xcode and the SFML library with it.
Keys.cfg.txt
Window_close 0:0
Fullscreen_toggle 5:89
Move 9:0 24:38
/////////////////////////////////////////
.CPP
#include "EventManager.h"
using namespace std;
EventManager::EventManager(): m_hasFocus(true){ LoadBindings(); }
EventManager::~EventManager(){
for (auto &itr : m_bindings){
delete itr.second;
itr.second = nullptr;
}
}
bool EventManager::AddBinding(Binding *l_binding){
if (m_bindings.find(l_binding->m_name) != m_bindings.end())
return false;
return m_bindings.emplace(l_binding->m_name, l_binding).second;
}
bool EventManager::RemoveBinding(std::string l_name){
auto itr = m_bindings.find(l_name);
if (itr == m_bindings.end()){ return false; }
delete itr->second;
m_bindings.erase(itr);
return true;
}
void EventManager::SetFocus(const bool& l_focus){ m_hasFocus = l_focus; }
void EventManager::HandleEvent(sf::Event& l_event){
// Handling SFML events.
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
EventType sfmlEvent = (EventType)l_event.type;
if (e_itr.first != sfmlEvent){ continue; }
if (sfmlEvent == EventType::KeyDown || sfmlEvent == EventType::KeyUp){
if (e_itr.second.m_code == l_event.key.code){
// Matching event/keystroke.
// Increase count.
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else if (sfmlEvent == EventType::MButtonDown || sfmlEvent == EventType::MButtonUp){
if (e_itr.second.m_code == l_event.mouseButton.button){
// Matching event/keystroke.
// Increase count.
bind->m_details.m_mouse.x = l_event.mouseButton.x;
bind->m_details.m_mouse.y = l_event.mouseButton.y;
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
break;
}
} else {
// No need for additional checking.
if (sfmlEvent == EventType::MouseWheel){
bind->m_details.m_mouseWheelDelta = l_event.mouseWheel.delta;
} else if (sfmlEvent == EventType::WindowResized){
bind->m_details.m_size.x = l_event.size.width;
bind->m_details.m_size.y = l_event.size.height;
} else if (sfmlEvent == EventType::TextEntered){
bind->m_details.m_textEntered = l_event.text.unicode;
}
++(bind->c);
}
}
}
}
void EventManager::Update(){
if (!m_hasFocus){ return; }
for (auto &b_itr : m_bindings){
Binding* bind = b_itr.second;
for (auto &e_itr : bind->m_events){
switch (e_itr.first){
case(EventType::Keyboard) :
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Mouse) :
if (sf::Mouse::isButtonPressed(sf::Mouse::Button(e_itr.second.m_code))){
if (bind->m_details.m_keyCode != -1){
bind->m_details.m_keyCode = e_itr.second.m_code;
}
++(bind->c);
}
break;
case(EventType::Joystick) :
// Up for expansion.
break;
default:
break;
}
}
if (bind->m_events.size() == bind->c){
auto callItr = m_callbacks.find(bind->m_name);
if(callItr != m_callbacks.end()){
callItr->second(&bind->m_details);
}
}
bind->c = 0;
bind->m_details.Clear();
}
}
void EventManager::LoadBindings(){
std::string delimiter = ":";
std::ifstream bindings;
bindings.open("keys.cfg");
if (!bindings.is_open()){ std::cout << "! Failed loading keys.cfg." << std::endl; return; }
std::string line;
while (std::getline(bindings, line)){
std::stringstream keystream(line);
std::string callbackName;
keystream >> callbackName;
Binding* bind = new Binding(callbackName);
while (!keystream.eof()){
std::string keyval;
keystream >> keyval;
int start = 0;
int end = keyval.find(delimiter);
if (end == std::string::npos){ delete bind; bind = nullptr; break; }
EventType type = EventType(stoi(keyval.substr(start, end - start)));
int code = stoi(keyval.substr(end + delimiter.length(),
keyval.find(delimiter, end + delimiter.length())));
EventInfo eventInfo;
eventInfo.m_code = code;
bind->BindEvent(type, eventInfo);
}
if (!AddBinding(bind)){ delete bind; }
bind = nullptr;
}
bindings.close();
}
The problem is that you have to copy the respective files in the bundle, and that only objective-c can provide you the full name of the file on the destination device.
To overcome this, make a .mm-file and place a c++ trampoline function in there, which gives you the full path (see code below).
One pitfall can be that you have to make sure that the config- and text files like "keys.cfg" are actually copied into the bundle. Select the respective file in the project and open the property inspector; make sure that - the respective target in "Target Membership" is checked.
// File: myFileNameProvider.mm
#import <Foundation/Foundation.h>
#include <iostream>
std::string GetTextureFilename(const char *name)
{
NSString *nameAsNSString = [NSString stringWithUTF8String:name];
NSString *fullName = [[NSBundle mainBundle]
pathForResource:nameAsNSString ofType: nil];
if (fullName)
return std::string([fullName UTF8String]);
else
return "";
}
Then, in your CPP-code, declare the signature of std::string GetTextureFilename(const char *name), and before opening the file get the full path by calling it:
// MyCPPFile.cpp
#include <iostream>
#include <fstream>
// declaration:
std::string GetTextureFilename(const char *name);
void myC_Func {
std::string fullPath = GetTextureFilename("keys.cfg");
std::ifstream bindings;
bindings.open(fullPath.c_str());
if (!bindings.is_open()) {
std::cout << "! Failed loading keys.cfg." << std::endl;
}
...
}
I'm new to programming and I'm trying to code a function that gets the shortest string from a list, but everytime I run it, visual studio shows an error "Exception thrown: read access violation". Where is the mistake?
#include <iostream>
#include <string>
using namespace std;
const string &shortest_string(initializer_list<string> strings) {
string *shortest_one = nullptr;
for (string string : strings) {
if (shortest_one == nullptr) shortest_one = &string;
else {
if (string.size() < shortest_one->size()) shortest_one = &string;
}
}
return *shortest_one;
}
int main() {
cout << shortest_string({ "hello" , "my", "name", "is", "dan" }) << endl;
return 0;
}
if (shortest_one = nullptr) is not a comparison operation. It is an assignment, that sets shortest_one to nullptr. This operation evaluates to 0, so the if expression is equivalent to if (0), or if (false).
Then in the else block, you are using shortest_one->size() but shortest_one is null...
Try to use if (shortest_one == nullptr) instead.
You made variable with name that matches type name (string variable, string type?). Plus there is issue that you return pointer to object with local scope of life. That is UB. Using iterators your function would work like this:
const string shortest_string(initializer_list<string> strings) {
if(!strings.size()) return string();
auto shortest_one = strings.begin();
for (auto it = shortest_one+1; it < strings.end(); it++ )
{
shortest_one = (it->size()< shortest_one->size()) ? it : shortest_one;
}
return *shortest_one;
}
I am working with cpp to build a project.
My project needs a file to do some configuration and I decided to use a file with JSON format. Here is an example:
{
"agentname":"agent1",
"server":[
{"ip":"192.168.0.1"},
{"port":"9999"}
]
}
Now I need to read this file so I use the JSON_Spirit. Here is my code:
ifstream conf("config", ios::in);
json_spirit::mValue mvalue;
json_spirit::read(conf, mvalue);
json_spirit::mObject obj = mvalue.get_obj();
string agentname = obj.find("agentname")->second.get_str();
After the code, I can get agentname.
But I don't know how to get ip and port.
I have tried like this:
string ip = obj.find("server")->second.find("ip")->second.get_str();
I think it should be something like this but the code above doesn't work.
I find that with json_spirit it helps to have a few utility accessor functions. Also, take care to examine the actual contents of the document:
This will work:
#include <json_spirit.h>
#include <iostream>
#include <sstream>
using namespace std;
const string test_str =
R"json({
"agentname":"agent1",
"server":[
{"ip":"192.168.0.1"},
{"port":"9999"}
]
}
)json";
json_spirit::mValue read_document(std::istream& is) {
json_spirit::mValue result;
auto ok = json_spirit::read(is, result);
if (!ok) {
throw std::runtime_error { "invalid json" };
}
return result;
}
const json_spirit::mValue& get_object_item(const json_spirit::mValue& element, const std::string& name)
{
return element.get_obj().at(name);
}
const json_spirit::mValue& get_array_item(const json_spirit::mValue& element, size_t index)
{
return element.get_array().at(index);
}
int main()
{
istringstream conf(test_str);
auto doc = read_document(conf);
const auto& agentname = get_object_item(doc, "agentname");
const auto& server = get_object_item(doc, "server");
const auto& ip_holder = get_array_item(server, 0);
const auto& ip = get_object_item(ip_holder, "ip");
const auto& port = get_object_item(get_array_item(server, 1), "port");
cout << agentname.get_str() << endl
<< ip.get_str() << endl
<< port.get_str() << endl;
return 0;
}
expected output:
agent1
192.168.0.1
9999
TL;DR: always remember that std::vector needs to move your data around when it grows, which invalidates any pointers you still have floating around.
I've googled around for this problem a bit, and it seems every case I came across was a question of calling delete on the same pointer twice. I'm writing a small program and I'm getting heap corruption, but the only thing doing heap allocation is the c++ standard library. I have a hunch I'm leaking a reference to a local variable or done something wrong with polymorphism, but I can't figure it out.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
struct Project;
struct Solution;
struct Line {
string command;
vector<string> params;
void print(ostream &os) {
os << command << ": ";
for (string s : params)
os << s << ' ';
os << endl;
}
};
struct Properties {
vector<string> includes;
vector<string> libPaths;
vector<string> libs;
vector<string> sources;
vector<string> headers;
vector<Project *> depends;
string folder;
string name;
string type;
};
struct Project : Properties {
Project() { built = false; }
bool built;
void build() {
if (built)
return;
built = true;
for (Project *p : depends)
p->build();
cout << "Building project: " << name << endl;
}
};
struct Solution : Properties {
public:
Project *getProject(const string &name) {
for (Project &p : projects) {
if (p.name == name)
return &p;
}
// No project with such a name -- create it
Project p;
cout << &p << endl;
p.name = name;
projects.push_back(p);
cout << "Created project: " << name << endl;
return getProject(name);
}
private:
vector<Project> projects;
};
Line parseLine(const string &strline) {
istringstream stream(strline);
Line line;
stream >> line.command;
while (stream.good()) {
string tok;
stream >> tok;
if (tok.length() > 0)
line.params.push_back(tok);
}
return line;
}
template <typename T>
vector<T> concat(const vector<T> &a, const vector<T> &b) {
vector<T> vec;
for (T obj : a)
vec.push_back(obj);
for (T obj : b)
vec.push_back(obj);
return vec;
}
template <typename T>
void printVector(ostream os, vector<T> v) {
for (T obj : v)
os << obj;
os << endl;
}
int main(int argc, char *argv[]) {
Solution solution;
Properties *properties = &solution;
ifstream stream("testproj.txt");
Project p[100]; // No error here....
string linestr;
for (int lineNum = 1; getline(stream, linestr); lineNum++) {
Line line = parseLine(linestr);
if (line.command == "solution") {
// Make future commands affect the solution
properties = &solution;
} else if (line.command == "exe" || line.command == "lib") {
if (line.params.size() != 1) {
cerr << "Error at line " << lineNum << endl;
return 1;
}
// Make future commands affect this project
properties = solution.getProject(line.params[0]);
properties->type = line.command;
properties->name = line.params[0];
} else if (line.command == "includes") {
properties->includes = concat(properties->includes, line.params);
} else if (line.command == "libpath") {
properties->libPaths = concat(properties->libPaths, line.params);
} else if (line.command == "libs") {
properties->libs = concat(properties->libs, line.params);
} else if (line.command == "folder") {
if (line.params.size() != 1) {
cerr << "Error at line " << lineNum << endl;
return 1;
}
properties->folder = line.params[0];
} else if (line.command == "source") {
properties->sources = concat(properties->sources, line.params);
} else if (line.command == "header") {
properties->headers = concat(properties->headers, line.params);
} else if (line.command == "depends") {
Project *proj;
for (string projName : line.params) {
proj = solution.getProject(projName);
properties->depends.push_back(proj);
}
}
}
}
The error:
HEAP: Free Heap block 00395B68 modified at 00395BAC after it was freed
Here is my stack trace (sorry no line numbers in the source above):
crashes in malloc & ntdll somewhere up here
libstdc++ ---- incomprehensible name mangling
main.cpp, line 24 (inside Properties::Properties()): (compiler-generated constructor)
main.cpp, line 37 (inside Project::Project()): Project() { built = false; }
main.cpp, line 62 (inside Solution::getProject()): Project p;
main.cpp, line 150 (inside main()): proj = solution.getProject(projName);
It seems to be crashing in the default constructor for Properties? Perhaps while constructing a vector?
Edit:
The input file, if it would help:
solution
includes deps/include deps/include/SDL2
libpath deps/lib
libs opengl32 glu32 SDL2main SDL2 libpng16 glew
exe game
folder game
source main.cpp
depends render common
lib render
folder render
source Shader.cpp
header TODO
depends common
lib common
folder common
source util.cpp
header TODO
This is a lot of code, but one strong possibility is that you are de-referencing one of the pointers returned by getProject, but this has been invalidated because the vector projects, that holds the objects pointed to, has performed a re-allocation. This invalidates all pointers, references and iterators.
When you do this:
projects.push_back(p);
projects may need to grow, which results in a re-allocation and the invalidation of pointers mentioned above.
Without looking into the code in any depth, it looks like you can implement Solution quite trivially by using an std::map:
struct Solution : Properties
{
public:
// Check for project with name "name"
// Add one if it doesn't exist
// return it
Project& getProject(const std::string& name)
{
if (!projects.count(name))
{
projects[name].name = name;
}
return projects[name];
}
// Return project with name "name" or raise exception
// if it doesn't exist
const Project& getProject(const string &name) const
{
return projects.at(name);
}
private:
std::map<std::string, Project> projects;
};
I am working on an application that builds a vector of structs for items in a given directory and returns a reference of the vector for it to be read, I receive the following errors when attempting to compile the example code below:
1. 'class std::vector<indexStruct, std::allocator<indexStruct> >' has no member named 'name'
2. no matching function for call to `std::vector<indexStruct, std::allocator<indexStruct> >::push_back(std::vector<indexStruct, std::allocator<indexStruct> >&)'
exampleApp.cpp
#include "exampleApp.h"
exampleApp::exampleApp()
{
this->makeCatalog();
}
char* findCWD()
{
char* buffer = new char[_MAX_PATH];
return getcwd(buffer, _MAX_PATH);
}
void exampleApp::makeCatalog()
{
char* cwd = this->findCWD();
vector<indexStruct> indexItems;
this->indexDir(cwd, indexItems);
}
void exampleApp:indexDir(char* dirPath, vector<indexStruct>& indexRef)
{
DIR *dirPointer = NULL;
struct dirent *dirItem = NULL;
vector<indexStruct> indexItems;
vector<indexStruct> indexItem;
try
{
if ((dirPointer = opendir(dirPath)) == NULL) throw 1;
while (dirItem = readdir(dirPointer))
{
if (dirItem == NULL) throw 2;
if (dirItem->d_name[0] != '.')
{
indexItem.name = dirItem->d_name;
indexItem.path = dirPath;
indexItems.push_back(indexItem);
indexItem.clear();
}
}
indexRef.swap(indexItems);
closedir(dirPointer);
}
catch(int errorNo)
{
//cout << "Caught Error #" << errorNo;
}
}
exampleApp.h
#ifndef EXAMPLEAPP_H
#define EXAMPLEAPP_H
#include <iostream.h>
#include <dirent.h>
#include <stdlib.h>
#include <vector.h>
using namespace std;
struct indexStruct
{
char* name;
char* path;
};
class exampleApp
{
public:
exampleApp();
private:
char* findCWD();
void makeCatalog();
void indexDir(char* dirPath, vector<indexStruct>& indexRef);
};
#endif
What am I doing wrong here, and is there a better way going about this?
You've made 'indexItem' a vector, you probably just want it to be the type you want to put in 'indexItems'. Also, I'd create the new struct in your loop:
while (dirItem = readdir(dirPointer))
{
if (dirItem == NULL) throw 2;
if (dirItem->d_name[0] != '.')
{
indexStruct indexItem;
indexItem.name = dirItem->d_name;
indexItem.path = dirPath;
indexItems.push_back(indexItem);
}
}
You are defining a vector called indexItem:
vector<indexStruct> indexItem;
This is just an array. So the following lines must be changed to reference a specific element of the vector:
indexItem.name = dirItem->d_name;// should be indexItem[..].name or indexItem.at(..).name
indexItem.path = dirPath; // same as above!