cocos2d-x JSON file parsing - c++

I have a .json file, containing an array of dictionaries. Can you show me a good way of parsing it? I'm using the cocos2d-x 3.0-alpha version and the json classes, placed in the external/json directory.
I tried:
Array* items = Array::createWithContentsOfFile("test.json");
and
string fullPath = CCFileUtils::getInstance()->fullPathForFilename("test.json");
long bufferSize = 0;
const char* mFileData = (const char*)FileUtils::getInstance()->getFileData(fullPath.c_str(), "r", &bufferSize);
string clearData(mFileData);
size_t pos = clearData.rfind("}");
clearData = clearData.substr(0, pos+1);
string data = clearData.c_str();
log("%s", clearData.c_str());
Json::Value _root;
Json::Reader reader;
reader.parse(data, _root);
but none of them work - the first method returns an empty array, the second one results a _root variable, containing the whole json, but I can't make it into an array and create a separate dictionary object for each of the array's elements ( which is what I'm trying to do ).

Use JsonCPP you mentioned above but with CCFileUtils class
unsigned long filesize = 0;
std::string content;
std::string fullPath = "path relative to your androidmanifest.xml/index.json"
unsigned char* fileData = CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(), "r", &filesize);
content.append((char*)fileData);
delete[] fileData;
Json::Value jsonresult;
Json::Reader reader;
bool parsingSuccessful = reader.parse( content, jsonresult );
if ( !parsingSuccessful )
{
// report to the user the failure
return false;
}

Related

How to read an INI file entirley?

I want to read an INI file entirely, I am using wxFileConfig class to do that but the most examples of the internet just to reading & writing only an item, not an entire INI file.
The data in the INI file similar to the following:
[sdl]
fullresolution=0x0
fullscreen=true
output=opengl
autolock=false
[dosbox]
machine=svga_s3
memsize=16
[render]
frameskip=0
aspect=false
scaler=normal2x
[cpu]
core=normal
cputype=auto
cycles=10000
cycleup=1000
cycledown=1000
.....
I tried to do something, but it just reads the headers ([sdl], [dosbox], [render], ...).
wxFileConfig config(wxEmptyString, wxEmptyString, wxEmptyString, wxGetCwd() + "\\dosbox.conf");
wxString str;
long idx;
bool bCont = config.GetFirstGroup(str, idx);
while (bCont) {
bCont = config.GetNextGroup(str, idx);
debugMsg("%s", str);
}
How to read each header with its items?
Taken from the documentation you can read all the entries like so:
// enumeration variables
wxString str;
long dummy;
// first enum all entries
bool bCont = config->GetFirstEntry(str, dummy);
while ( bCont ) {
aNames.Add(str);
bCont = config->GetNextEntry(str, dummy);
}
It's very similar to the code you have to read the all the groups.
I found a complete code that brings all data from an .ini file:
wxFileConfig config(wxEmptyString, wxEmptyString, wxEmptyString, wxGetCwd() + "\\dosbox.conf");
wxString group;
long group_index;
config.SetPath("/");
bool has_group = config.GetFirstGroup(group, group_index);
while (has_group) {
config.SetPath(group);
wxString entry;
long entry_index;
bool has_entry = config.GetFirstEntry(entry, entry_index);
while (has_entry) {
wxString value = config.Read(entry, "");
wxMessageOutputDebug d;
d.Printf("[%s] %s = %s", group, entry, value);
has_entry = config.GetNextEntry(entry, entry_index);
}
config.SetPath("/");
has_group = config.GetNextGroup(group, group_index);
}
The source.

How to save char* tag values in libexif

I'm having an issue with libexif saving tags string data. I'm allocating memory for string value, using strcpy and then just assiggn pointer to specific tags entry->data. Problem is - saving with exif_data_save_data and fwrite, i get my string value cutted to 7 chars. If I load all this data at first from file with longer string value for this specific exif tag (EXIF_TAG_NAME in particular), max string len coud be different.
static char* get_c_str(QString qs)
{
QByteArray *qba = new QByteArray(qs.toLatin1());
char* str = (char *)malloc(qba->count());
strcpy(str,qba->data());
return str;
}
static ExifEntry *init_tag(ExifData *exif, ExifIfd ifd, ExifTag tag)
{
ExifEntry *entry;
if (!((entry = exif_content_get_entry (exif->ifd[ifd], tag)))) {
entry = exif_entry_new ();
assert(entry != NULL);
entry->tag = tag;
exif_content_add_entry (exif->ifd[ifd], entry);
exif_entry_initialize (entry, tag);
exif_entry_unref(entry);
}
return entry;
}
void MainWindow::on_writeButton_clicked()
{
if(!buf)
return;
ent = init_tag(ed,ExifIfd::EXIF_IFD_0,ExifTag::EXIF_TAG_MODEL);
ent->data = (unsigned char*)get_c_str(ui->modelL->text());
................................................
exif_data_save_data(ed, &exifData, &exifDatLen);
f = fopen(path, "wb");
fwrite(exif_header,exif_header_len,1,f);
fputc((exifDatLen+2)>>8,f);
fputc((exifDatLen+2) & 0xff, f);
fwrite(exifData,exifDatLen,1,f);
fwrite(buf+image_data_offset,flen,1,f);
fclose(f);
}
Expected Camera Model to be "Canon EOS 5D Mark II" but was "Canon E"

Issue to file a vector list

I'm using a vector of struct in order to create a kind of minimal filesystem. The entry point is a FileSystem struct manage by MTP but it contain to much data and it also force me to deploy mtp stuff even at high-level.
What I have done is to parse the list of files provided by MTP and file a vector of struct.
here is what I have in the cpp file
std::vector<FileObject*> mtp_wrapper::ListsOfFirstLevel(uint32_t idx) {
std::vector<FileObject*> ListOfObjects;
LIBMTP_file_t *AllFiles;
LIBMTP_file_t *file;
AllFiles = getListOfFiles();
file = AllFiles;
while(file !=NULL) {
LIBMTP_file_t *oldfile;
FSitem = new FileObject();
if(file->parent_id == idx) {
FSitem->filename = file->filename;
FSitem->filesize = file->filesize;
if(file->filetype == LIBMTP_FILETYPE_FOLDER)
FSitem->isFolder = true;
else
FSitem->isFolder = false;
FSitem->itemid = file->item_id;
FSitem->parent_id = file->parent_id;
FSitem->lastmodified = file->modificationdate;
ListOfObjects.push_back(FSitem);
// free(FSitem)
}
oldfile = file;
file = file->next;
LIBMTP_destroy_file_t(oldfile);
}
return ListOfObjects;
}
FileObject is declared in a header file as below
struct FileObject {
uint32_t itemid;
bool isFolder;
char *filename;
uint64_t filesize;
LIBMTP_filetype_t filetype;
uint32_t location;
uint32_t parent_id;
time_t lastmodified;
};
The code to file the FSitem is working fine, FSItem is file correctly but at the end of while, the ListOfOjects is empty. seems like the push_back fail...
ListOfObjects.push_back(FSitem);
// free(FSitem)
Why do you free FSItem ? You have a std::vector of pointers. When you call push_back you copy the pointer to your item inside the vector. The item hasn't moved and hasn't been copied. If you free FSItem , you delete the object, and then the pointer in your vector will be invalid.

Converting from Java to C++ Reading a File, and parsing

The Text file looks like this:
Apple,Itunes,1,7.3
Microsoft,Windows Media Player,1,10
.... and so on.....
The parse method is:
private IApplication parseLineToApp(String lineFromTxtFile) {
Scanner lineScanner = new Scanner(lineFromTxtFile);
lineScanner.useDelimiter(",");
return new Application(lineScanner.next(), lineScanner.next(), lineScanner.nextInt(), lineScanner.next());
}
I want to do the same thing in c++ to create a new application().
Note: I already have an application class, and need to add that application to a repository which is a collection of applications
Thanks in advance :)
You can create a vector of strings using Boost and the STL.
// given std::string lineFromTxtFile
std::vector<std::string> scanner;
boost::split (scanner, lineFromTxtFile, boost::is_any_of(","));
return new Application (scanner[0], scanner[1], scanner[2], scanner[3]);
If you want scanner[2] to be an integer, there's
boost::lexical_cast<int> (scanner[2])
File operations can be done a few different ways in C/C++. A C++-style approach could look like the following:
std::vector<Application> ApplicationList;
std::ifstream fin("myapplist.txt"); // open a file stream
while (!fin.eof()) // while this isn't the end of the file
{
char buffer[256] = {0};
fin.getline(buffer, 256); // read the current line of text into the buffer
std::vector<std::string> scanner;
boost::split(scanner, buffer, boost::is_any_of(",")); // split the buffer and store the results in the scanner vector
ApplicationList.push_back(Application(scanner[0], scanner[1], scanner[2], scanner[3]); // add the application to the application list
}
fin.close();
Assuming your Application struct/class is copyable (otherwise you'd want to use pointers in your ApplicationList).
There is no Scanner class in the C++ standard library, the direct translation is not possible. Of course, you can always implement your own.
Alas, you could create a split function:
unsigned int split(const std::string &txt, std::vector<std::string> &strs, char ch)
{
size_t pos = txt.find( ch );
size_t initialPos = 0;
strs.clear();
if ( pos != std::string::npos ) {
// Decompose each statement
strs.push_back( txt.substr( initialPos, pos - initialPos + 1 ) );
initialPos = pos + 1;
pos = txt.find( ch, initialPos );
while( pos != std::string::npos ) {
strs.push_back( txt.substr( initialPos, pos - initialPos + 1 ) );
initialPos = pos + 1;
pos = txt.find( ch, initialPos );
}
}
else strs.push_back( txt );
return strs.size();
}
Then you could rewrite your code as:
std::vector<std::string> scanner;
split( lineFromTxtFile, scanner, ',' );
return new Application (scanner[0], scanner[1], scanner[2], scanner[3]);
Hope this helps.

RapidXML, reading and saving values

I've worked myself through the rapidXML sources and managed to read some values. Now I want to change them and save them to my XML file:
Parsing file and set a pointer
void SettingsHandler::getConfigFile() {
pcSourceConfig = parsing->readFileInChar(CONF);
cfg.parse<0>(pcSourceConfig);
}
Reading values from XML
void SettingsHandler::getDefinitions() {
SettingsHandler::getConfigFile();
stGeneral = cfg.first_node("settings")->value();
/* stGeneral = 60 */
}
Changing values and saving to file
void SettingsHandler::setDefinitions() {
SettingsHandler::getConfigFile();
stGeneral = "10";
cfg.first_node("settings")->value(stGeneral.c_str());
std::stringstream sStream;
sStream << *cfg.first_node();
std::ofstream ofFileToWrite;
ofFileToWrite.open(CONF, std::ios::trunc);
ofFileToWrite << "<?xml version=\"1.0\"?>\n" << sStream.str() << '\0';
ofFileToWrite.close();
}
Reading file into buffer
char* Parser::readFileInChar(const char* p_pccFile) {
char* cpBuffer;
size_t sSize;
std::ifstream ifFileToRead;
ifFileToRead.open(p_pccFile, std::ios::binary);
sSize = Parser::getFileLength(&ifFileToRead);
cpBuffer = new char[sSize];
ifFileToRead.read( cpBuffer, sSize);
ifFileToRead.close();
return cpBuffer;
}
However, it's not possible to save the new value. My code is just saving the original file with a value of "60" where it should be "10".
Rgds
Layne
I think this is a RapidXML Gotcha
Try adding the parse_no_data_nodes flag to cfg.parse<0>(pcSourceConfig)
You should definitely be testing that the output file opened correctly and that your write succeeded. At the simplest, you need something like:
if ( ! ofFileToWrite << "<?xml version=\"1.0\"?>\n"
<< sStream.str() << '\0' ) {
throw "write failed";
}
Note that you don't need the '\0' terminator, but it shouldn't do any harm.
Use the following method to add an attribute to a node. The method uses the allocation of memory for strings from rapidxml. So rapidxml takes care of the strings as long as the document is alive. See http://rapidxml.sourceforge.net/manual.html#namespacerapidxml_1modifying_dom_tree for further information.
void setStringAttribute(
xml_document<>& doc, xml_node<>* node,
const string& attributeName, const string& attributeValue)
{
// allocate memory assigned to document for attribute value
char* rapidAttributeValue = doc.allocate_string(attributeValue.c_str());
// search for the attribute at the given node
xml_attribute<>* attr = node->first_attribute(attributeName.c_str());
if (attr != 0) { // attribute already exists
// only change value of existing attribute
attr->value(rapidAttributeValue);
} else { // attribute does not exist
// allocate memory assigned to document for attribute name
char* rapidAttributeName = doc.allocate_string(attributeName.c_str());
// create new a new attribute with the given name and value
attr = doc.allocate_attribute(rapidAttributeName, rapidAttributeValue);
// append attribute to node
node->append_attribute(attr);
}
}