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
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.
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"
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.
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.
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);
}
}