C/C++ Plist reader/parser?

A little off topic, but I was wondering if anyone has come across a cross platform C/C++ plist parsing library? I know Cocos2d-x has some built in functionality to read plists, but I am looking for a library to integrate into a few tools I am working on. After hours of googling, and coming up short, so I figured I would at least posit the question. I know .plist files are a “subset” of XML, but I honestly know next to nothing about XML, or how to parse it correctly. What what I have seen so far, it doesnt look like it would be a wise project to take on myself. I have been researching XML parsers for a day or so now, and have found “TinyXML” and “pugiXML” to be the top candidates, but again, I have found nothing with those libs that will help parse a plist. A list of “requirements” is listed below. Any ideas? Thanks in advance!

Requirements:

  • Cross platform (standard C/C++)
  • STL is ok, however “boost” is not
  • Open Source would be preferred since my plan is to run on as many OSs as possible
  • Small in size (target os is still iOS / android)
  • Fast (for obvious reasons)

Cocos2d-x uses libxml2 to parse plist files. You can refer the source in CCSAXParser.cpp.
I recommend libxml2 coz both iOS & Android NDK have integrated it, you dont’ need to do extra jobs.

Thanks for the info, but I decided to go another way…. I found that actually writing a class using pugiXML to parase PList data into something useful was actually pretty easy. Should be cross platform, and only requires 1 .h and 1 .cpp to be added, thus I dont have to worry about precompiling any libs.

Can i add a question to this thread ?

Can anyone verify that parsing plists on android is slow ? iOS reads them in a split second (using nsdictionary), but while reading the same plist file on the android (witch i believe is CCSAXParser using libxml2), it took more then 2 minutes… i even tried to optimize my plist file, and got a huge diference, from 2 minutes to 30 seconds, but that still is a long parsing time for just a simple XML. Maybe i should try another alternative ?

My plist file consists of many nodes, and each node has some informations. Maybe if i share those informations on a single node it would be a lot faster ? Should i use less nodes and more string information (to split using c++) latter ?

Resource files, including plist file, are packed into .apk, which is a zip file.
So, when parsing plist files, should unzip it first and then parse the file.
I think the unzipping operation may take some time.

Ok, to contribute to this thread in future references, i found out why my plist file was taking too long to parse. CCSAXParser is very slow parsing plist files (at least plist files as big as mine where, witch is more then > 600 kb).

I ended up using pugixml and having to convert all my plist files into xml files, the pain was worth it, it compiles nicely on android and iOS (having to take out the STL). From 30 seconds on my smallest plist file i went to less then 3 seconds to parse it. Big difference. iOS gave me similar results, just slightly slower then using nsdictionary, but well worth it.

Thank you for your sharing, I didn’t know the difference.

Confirmed. parsing a plist which is 855KB (5 Level of Dictionary , that is Numbers in Dictionaries in Dictionaries in Dictionaries in Dictionaries of Dictionary), takes about 1 minite on the latest android hardware( Galaxy Nexus).

Our Manager says that that no one would like to wait such long to loading a screen even if we having a loading screen.

From a company perspective, we have to find out a solution, either using something else to parse XML , or break down the plist file (both are pain for a company).

Hope this issue could be fixed soon.

I also use a lot of plist files in my current project, and right now it takes several minutes to load on Android compared to almost instant on iOS. Any suggestions on how to solve the problem?

Yes, convert your plist files to a xml file, then either use pugixml or another xml parser list. I took that route and my data file loading went from 30 seconds to less then 2 seconds. Recommend!

At the moment plist parsing on android is way to slow because lib2xml… under iOS is ok because it uses the iOS parsing system, witch is way better.

Did you use any tool to convert your plist files to XML?

In my case, i coded our game editor, so it was just a matter of changing my editor to write a xml encoded file instead a plist parsed file.

You could write a simple program to parse your plist and then convert everything to memory objects, then use a xml parser like pugi to write the xml file using your objects in iOS. There might be a plist to xml converter out there if your dont want to write a program just to convert. But i do recommend you write your own, this way your xml will be more optimized and you learn a thing or two on the way…

I actually wrote an entire Plist parser using pugixml that is very fast, and easy to use. It started as a fix for this issue (see how I am the first post :slight_smile: ). It even borrows some of the utility functions from Cocos2d like “splitWithForm”, but has been completely overhauled to be used in the engine I have created. If someone would like to take a few mins to convert the engine specific stuff to Cocos2d (stuff like rects, and points) then I can give you guys that. Any takers?

Sounds great, I would definitely want to give it a go =)

You will need a copy of pugixml. The version I am using has been modified to allow C**11 features, but the stock version should work fine for you. Anything beginning with DS_ is from my engine and will need to be modified to suit you needs. It should be fairly easy to migrate between the 2 since the rect and point formats were designed to be compatible with cocos2d to begin with.
The Dictionary has the ability to read, write, modify, etc et etc any apple formated plist. The only limitation that I am aware of is Arrays MUST NOT contain other arrays or dictionaries. Thus arrays are POD ONLY! Let me know if this is helpful.

Here is the .h
<pre>
#ifndef DS_Dictionary_h
#define DS_Dictionary_h
#include “pugixml.hpp”
using namespace pugi;
class DS_Dictionary
{
private:
//Data
xml_document doc;
vector<xml_node> dictTree;
//Constructor blockers
DS_Dictionary; //Copy constructor - blocker
DS_Dictionary& operator= ; //Assignment - blocker
//Methods
string cleanStringWhiteSpace;
void split;
bool splitWithForm;
bool rectFromString;
bool vec2FromString;
public:
//Constructor
DS_Dictionary;
//Methods
bool loadRootSubDictFromFile;
bool saveRootSubDictToFile;
bool stepIntoSubDictWithKey;
void stepOutOfSubDict;
void stepBackToRootSubDict;
uint getNumKeys;
string getKey;
vector getAllKeys;
uint getIndexOfKey;
uint getIndexOfKeyWithClosestAlphaNumericalMatch;
void removeKey;
void removeKey;
void removeAllKeys;
int getIntegerForKey;
bool getBoolForKey;
float getFloatForKey;
string getStringForKey;
DS_Vec2 getVec2ForKey;
DS_Rect getRectForKey;
vector getStringArrayForKey;
vector<DS_Vec2> getVec2ArrayForKey;
vector<DS_Rect> getRectArrayForKey;
void setSubDictForKey;
void setSubDictForKey;
void setIntegerForKey;
void setIntegerForKey;
void setBoolForKey;
void setBoolForKey;
void setFloatForKey;
void setFloatForKey;
void setStringForKey;
void setStringForKey;
void setVec2ForKey;
void setVec2ForKey;
void setRectForKey;
void setRectForKey;
void setStringArrayForKey;
void setStringArrayForKey;
void setVec2ArrayForKey;
void setVec2ArrayForKey;
void setRectArrayForKey;
void setRectArrayForKey;
};
#endif
</pre>
Here is the .cpp
<pre>
#include “DS_Dictionary.h”
//Constructor
DS_Dictionary::DS_Dictionary
{
//Initializers
dictTree.push_back);
//Create default format
xml_node plistNode = doc.append_child;
plistNode.append_attribute = “1.0”;
plistNode.append_child;
//Set root dictTree node
dictTree.back = doc.child.child;
}
//Private methods
string DS_Dictionary::cleanStringWhiteSpace
{
string cleanStr = _str;
cleanStr.erase, cleanStr.end, static_cast<int(*)(int)>), cleanStr.end);
return cleanStr;
}
void DS_Dictionary::split
{
int nend=0;
int nbegin=0;
while
{
nend = src.find;
if
vect.push_back~~nbegin));
else
vect.push_back);
nbegin = nend + strlen;
}
}
bool DS_Dictionary::splitWithForm
{
// string is empty
if 0){ return false; }

int nPosLeft  = content.find('{');
int nPosRight = content.find('}');

// don't have '{' and '}'
if(nPosLeft  string::npos || nPosRight  (int)string::npos){ return false; }

// '}' is before '{'
if(nPosLeft \> nPosRight){ return false; }

string pointStr = content.substr(nPosLeft + 1, nPosRight - nPosLeft - 1);

// nothing between '{' and '}'
if(pointStr.length()  0){ return false; }

int nPos1 = pointStr.find;
int nPos2 = pointStr.find;
// contain ‘{’ or ‘}’
ifstring::npos || nPos2 != string::npos){ return false; }
split;
if != 2 || strs[0].length 0 || strs[1].length() 0)
{
return false;
}
return true;
}
bool DS_Dictionary::rectFromString
{
//Clean white spaces
string content = cleanStringWhiteSpace;
// find the first ‘{’ and the third ‘}’
int nPosLeft = content.find;
int nPosRight = content.find;
for
{
if string::npos)
{
break;
}
nPosRight = content.find;
}
ifstring::npos || nPosRight (int)string::npos){ return false; }

content = content.substr(nPosLeft + 1, nPosRight - nPosLeft - 1);
int nPointEnd = content.find('}');
if(nPointEnd  string::npos){ return false; }

nPointEnd = content.find;
ifstring::npos){ return false; }
// get the point string and size string
string pointStr = content.substr;
string sizeStr = content.substr~~ nPointEnd);
// split the string with ‘,’
vector pointInfo;
if){ return false; }
vector sizeInfo;
if){ return false; }
float x = atof);
float y = atof);
float width = atof);
float height = atof);
rect = DS_Rect, DS_Vec2);
return true;
}
bool DS_Dictionary::vec2FromString
{
vector strs;
if, strs))
{
return false;
}
float x = atof);
float y = atof);
vec2 = DS_Vec2;
return true;
}
//Methods
bool DS_Dictionary::loadRootSubDictFromFile
{
//Clear existing
dictTree.clear;
dictTree.push_back);
//Open file
xml_parse_result result = doc.load_file;
if
{
if{ printf.append.append.append).c_str); }
return false;
}
//Set root dictTree node
dictTree.back = doc.child.child;
return true;
}
bool DS_Dictionary::saveRootSubDictToFile
{
//Attempt to save file
bool result = doc.save_file;
if
{
if{ printf.append.c_str); }
}
return result;
}
bool DS_Dictionary::stepIntoSubDictWithKey
{
for.child; node; node = node.next_sibling)
{
if string(key) && node.next_sibling() node.next_sibling)
{
dictTree.push_back);
return true;
}
}
if{ printf.append.c_str); }
return false;
}
void DS_Dictionary::stepOutOfSubDict
{
if > 1)
{
dictTree.pop_back;
}
}
void DS_Dictionary::stepBackToRootSubDict
{
if > 1)
{
dictTree.clear;
dictTree.push_back);
dictTree.back = doc.child.child;
}
}
uint DS_Dictionary::getNumKeys
{
uint numKeys = 0;
for.first_child; node; node = node.next_sibling.next_sibling)
{
numKeys**;
}

return numKeys;
}

string DS_Dictionary::getKey(uint index)
{
uint count = 0;

for(xml_node node = dictTree.back().first_child(); node; node = node.next_sibling().next_sibling())
{
if(count index){ return node.child_value(); }
count++;
}

return string();

}

vector<string> DS_Dictionary::getAllKeys()
{
vector<string> strings;

for(xml\_node node = dictTree.back().first\_child(); node; node = node.next\_sibling().next\_sibling())
{
    strings.push\_back(node.child\_value());
}

return strings;

}

uint DS_Dictionary::getIndexOfKey(const char* key)
{
uint count = 0;

for(xml\_node node = dictTree.back().child("key"); node; node = node.next\_sibling("key"))
{
    if(node.child\_value()  string(key))

{
return count;
}
count**;
}
return 0;
}
uint DS_Dictionary::getIndexOfKeyWithClosestAlphaNumericalMatch
{
uint count = 0;
for.child; node; node = node.next_sibling)
{
if))
{
return count;
}
count**;
}

return 0;
}

void DS_Dictionary::removeKey(uint index)
{
uint count = 0;

for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(count index)
{
//Remove the node
dictTree.back().remove_child(node.next_sibling());
dictTree.back().remove_child(node);
break;
}
count++;
}
}

void DS_Dictionary::removeKey(const char* key)
{
//Check to see if the value already exists withing the current sub dict
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() string(key))
{
//Already exists, remove the node
dictTree.back().remove_child(node.next_sibling());
dictTree.back().remove_child(node);
break;
}
}
}

void DS_Dictionary::removeAllKeys()
{
while(xml_node node = dictTree.back().child(“key”))
{
dictTree.back().remove_child(node.next_sibling());
dictTree.back().remove_child(node);
}
}

int DS_Dictionary::getIntegerForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() string(key) && (node.next_sibling() node.next_sibling(“integer”) || node.next_sibling() node.next_sibling(“real”)))
{
return strtol(node.next_sibling().child_value(), NULL, 10);
}
}

if(DS\_DEBUG\_MODE){ printf("%s \\n",string("DS\_ENGINE:\> :ERROR: FAILED TO READ INTEGER VALUE FOR KEY: ").append(key).c\_str()); }
return 0;

}

bool DS_Dictionary::getBoolForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() string(key) && (node.next_sibling() node.next_sibling(“true”) || node.next_sibling() node.next_sibling(“false”)))
{
if(node.next_sibling() node.next_sibling(“true”)){ return true; }
if(node.next_sibling() node.next_sibling(“false”)){ return false; }
}
}

if(DS_DEBUG_MODE){ printf(“s \n",string("DS_ENGINE:> :ERROR: FAILED TO READ BOOL VALUE FOR KEY: ").append(key).c_str()); }
return false;
}

float DS_Dictionary::getFloatForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() == string(key) && (node.next_sibling() == node.next_sibling(“integer”) || node.next_sibling() == node.next_sibling(“real”)))
{
return strtod(node.next_sibling().child_value(), NULL);
}
}

if(DS\_DEBUG\_MODE){ printf("s ”,string(“DS\_ENGINE:\> :ERROR: FAILED TO READ FLOAT VALUE FOR KEY:”).append(key).c\_str()); }

return 0;
}

string DS_Dictionary::getStringForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() string(key) && node.next_sibling() node.next_sibling(“string”))
{
return node.next_sibling().child_value();
}
}

if(DS_DEBUG_MODE){ printf(“s \n",string("DS_ENGINE:> :ERROR: FAILED TO READ STRING VALUE FOR KEY: ").append(key).c_str()); }
return “”;
}

DS_Vec2 DS_Dictionary::getVec2ForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() == string(key) && node.next_sibling() == node.next_sibling(“string”))
{
DS_Vec2 vec2;

        if(vec2FromString(node.next\_sibling().child\_value(), vec2))
        {
            return vec2;
        }

        else
        {
            if(DS\_DEBUG\_MODE){ printf("s ”,string(“DS\_ENGINE:\> :ERROR: MALFORMED VEC2 VALUE FOR KEY:”).append(key).c\_str()); }

return DS_Vec2();
}
}
}

if(DS_DEBUG_MODE){ printf(“s \n",string("DS_ENGINE:> :ERROR: FAILED TO READ VEC2 VALUE FOR KEY: ").append(key).c_str()); }
return DS_Vec2();
}

DS_Rect DS_Dictionary::getRectForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() == string(key) && node.next_sibling() == node.next_sibling(“string”))
{
DS_Rect rect;

        if(rectFromString(node.next\_sibling().child\_value(), rect))
        {
            return rect;
        }

        else
        {
            if(DS\_DEBUG\_MODE){ printf("s ”,string(“DS\_ENGINE:\> :ERROR: MALFORMED RECT VALUE FOR KEY:”).append(key).c\_str()); }

return DS_Rect();
}
}
}

if(DS_DEBUG_MODE){ printf(“s \n",string("DS_ENGINE:> :ERROR: FAILED TO READ RECT VALUE FOR KEY: ").append(key).c_str()); }
return DS_Rect();
}

vector<string> DS_Dictionary::getStringArrayForKey(const char* key)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(node.child_value() == string(key) && node.next_sibling() == node.next_sibling(“array”))
{
vector<string> value;

        for(xml\_node arrayNode = node.next\_sibling().first\_child(); arrayNode; arrayNode = arrayNode.next\_sibling())
        {
            value.push\_back(arrayNode.child\_value());
        }

        return value;
    }
}

if(DS\_DEBUG\_MODE){ printf("s ”,string(“DS\_ENGINE:\> :ERROR: FAILED TO READ STRING VALUE FOR ARRAY KEY:”).append(key).c\_str()); }

return vector();
}

vector<DS_Vec2> DS_Dictionary::getVec2ArrayForKey(const char* key)
{
vector strings = getStringArrayForKey(key);
vector<DS_Vec2> vectorVec2;

for(int i=0; i<strings.size(); i++)
{
vectorVec2.push_back(DS_Vec2());
if(!vec2FromString(strings[i], vectorVec2.back()))
{
if(DS_DEBUG_MODE){ printf("%s \n",string(“DS_ENGINE:> :ERROR: MALFORMED VEC2 VALUE FOR ARRAY KEY: “).append(key).c_str()); }
vectorVec2.clear();
break;
}
}
return vectorVec2;
}
vector<DS_Rect> DS_Dictionary::getRectArrayForKey(const char* key)
{
vector strings = getStringArrayForKey(key);
vector<DS_Rect> vectorRect;
for(int i=0; i<strings.size(); i++)
{
vectorRect.push_back(DS_Rect());
if(!rectFromString(strings[i], vectorRect.back()))
{
if(DS_DEBUG_MODE){ printf(”%s \n",string("DS_ENGINE:> :ERROR: MALFORMED RECT VALUE FOR ARRAY KEY:”).append(key).c_str()); }
vectorRect.clear();
break;
}
}

return vectorRect;
}

void DS_Dictionary::setSubDictForKey(const char* key){ setSubDictForKey(key, false); }
void DS_Dictionary::setSubDictForKey(const char* key, bool alphaNumericallySorted)
{
//Check to see if the sub dict already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
dictTree.back().insert_child_after(“dict”, keyNode);
}

void DS_Dictionary::setIntegerForKey(const char* key, int value){ setIntegerForKey(key, value, false); }
void DS_Dictionary::setIntegerForKey(const char* key, int value, bool alphaNumericallySorted)
{
//Create a string from the input value
stringstream stream;
stream << value;

//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
dictTree.back().insert_child_after(“integer”, keyNode).append_child(node_pcdata).set_value(stream.str().c_str());
}

void DS_Dictionary::setBoolForKey(const char* key, bool value){ setBoolForKey(key, value, false); }
void DS_Dictionary::setBoolForKey(const char* key, bool value, bool alphaNumericallySorted)
{
//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
if(value){ dictTree.back().insert_child_after(“true”, keyNode); }
else { dictTree.back().insert_child_after(“false”, keyNode); }
}

void DS_Dictionary::setFloatForKey(const char* key, float value){ setFloatForKey(key, value, false); }
void DS_Dictionary::setFloatForKey(const char* key, float value, bool alphaNumericallySorted)
{
//Create a string from the input value
stringstream stream;
stream << value;

//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
dictTree.back().insert_child_after(“real”, keyNode).append_child(node_pcdata).set_value(stream.str().c_str());
}

void DS_Dictionary::setStringForKey(const char* key, const string& value){ setStringForKey(key, value, false); }
void DS_Dictionary::setStringForKey(const char* key, const string& value, bool alphaNumericallySorted)
{
//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
dictTree.back().insert_child_after(“string”, keyNode).append_child(node_pcdata).set_value(value.c_str());
}

void DS_Dictionary::setVec2ForKey(const char* key, const DS_Vec2& value){ setVec2ForKey(key, value, false); }
void DS_Dictionary::setVec2ForKey(const char* key, const DS_Vec2& value, bool alphaNumericallySorted)
{
//Create a string from the input value
stringstream stream;
stream << “{”;
stream << value.x;
stream << “,”;
stream << value.y;
stream << “}”;

//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
dictTree.back().insert_child_after(“string”, keyNode).append_child(node_pcdata).set_value(stream.str().c_str());
}

void DS_Dictionary::setRectForKey(const char* key, const DS_Rect& value){ setRectForKey(key, value, false); }
void DS_Dictionary::setRectForKey(const char* key, const DS_Rect& value, bool alphaNumericallySorted)
{
//Create a string from the input value
stringstream stream;
stream << “{{”;
stream << value.origin.x;
stream << “,”;
stream << value.origin.y;
stream << “},{”;
stream << value.size.x;
stream << “,”;
stream << value.size.y;
stream << “}}”;

//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
dictTree.back().insert_child_after(“string”, keyNode).append_child(node_pcdata).set_value(stream.str().c_str());
}

void DS_Dictionary::setStringArrayForKey(const char* key, const vector& value){ setStringArrayForKey(key, value, false); }
void DS_Dictionary::setStringArrayForKey(const char* key, const vector& value, bool alphaNumericallySorted)
{
//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
xml_node arrayNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
arrayNode = dictTree.back().insert_child_after(“array”, keyNode);

//Fill the array
for(int i=0; i<value.size(); i++)
{
arrayNode.append_child(“string”).append_child(node_pcdata).set_value(value[i].c_str());
}
}

void DS_Dictionary::setVec2ArrayForKey(const char* key, const vector<DS_Vec2>& value){ setVec2ArrayForKey(key, value, false); }
void DS_Dictionary::setVec2ArrayForKey(const char* key, const vector<DS_Vec2>& value, bool alphaNumericallySorted)
{
//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
xml_node arrayNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
arrayNode = dictTree.back().insert_child_after(“array”, keyNode);

//Fill the array
for(int i=0; i<value.size(); i++)
{
//Create a string from the input value
stringstream stream;
stream << “{”;
stream << value[i].x;
stream << “,”;
stream << value[i].y;
stream << “}”;

    arrayNode.append_child("string").append_child(node_pcdata).set_value(stream.str().c_str());
}

}

void DS_Dictionary::setRectArrayForKey(const char* key, const vector<DS_Rect>& value){ setRectArrayForKey(key, value, false); }
void DS_Dictionary::setRectArrayForKey(const char* key, const vector<DS_Rect>& value, bool alphaNumericallySorted)
{
//Check to see if the value already exists withing the current sub dict
removeKey(key);

//Create a key value pair as a child of the current sub dict
xml_node keyNode;
xml_node arrayNode;
bool appendKey = true;
if(alphaNumericallySorted)
{
for(xml_node node = dictTree.back().child(“key”); node; node = node.next_sibling(“key”))
{
if(!alphaNumericallyLessThan(node.child_value(), key))
{
keyNode = dictTree.back().insert_child_before(“key”, node);
appendKey = false;
break;
}
}
}

if(appendKey){ keyNode = dictTree.back().append_child(“key”); }
keyNode.append_child(node_pcdata).set_value(key);
arrayNode = dictTree.back().insert_child_after(“array”, keyNode);

//Fill the array
for(int i=0; i<value.size(); i++)
{
//Create a string from the input value
stringstream stream;
stream << “{{”;
stream << value[i].origin.x;
stream << “,”;
stream << value[i].origin.y;
stream << “},{”;
stream << value[i].size.x;
stream << “,”;
stream << value[i].size.y;
stream << “}}”;

    arrayNode.append_child("string").append_child(node_pcdata).set_value(stream.str().c_str());
}

}

Here are some support functions that will need to be included somewhere

static string itos(int number)
{
    stringstream ss;
    ss << number;
    return ss.str();
}

static string ftos(float number)
{
    stringstream ss;
    ss << number;
    return ss.str();
}

static bool alphaNumericallyLessThan(const string& s1, const string& s2)
{ 
    //AlphaNumeric test...
    uint tSize = s1.size() < s2.size() ? s1.size() : s2.size();

    for(int i=0; i= 97 && s1[i] <= 122) ? s1[i] - 32 : s1[i];
        uint c2 = (s2[i] >= 97 && s2[i] <= 122) ? s2[i] - 32 : s2[i];

        if(c2 > c1){ return true; }
        if(c2 < c1){ return false; }
    }

    //Default return
    return true;
}

static void sortAlphaNumerically(vector& strings)
{
    //Return if empty
    if(strings.size() == 0){ return; }

    //Sort
    vectorsortedStrings;
    sortedStrings.push_back(strings[0]);

    for(int i=1; i& splitStr)
{
    //Clear incomming
    splitStr.clear();

    //Split
    const char *start = str.c_str();
    const char *end   = start + str.size();
    const char *token = start;

    while(start!=end)
    {
        if(isspace(*start))
        {
            if(token < start)
            { 
                splitStr.push_back(string(token, start));
                if(splitStr.back() == " "){ splitStr.pop_back(); } //Dont allow empty
            }

            start++;
            token = start;
        }

        else{ start++; }
    } 

    if(token < start)
    { 
        splitStr.push_back(string(token, start));
        if(splitStr.back() == " "){ splitStr.pop_back(); } //Dont allow empty
    }
}

Here is an example usage

    //Load rootDict from file, and then step into the metadata sub dict.
    DS_Dictionary rootDict;
    if(!rootDict.loadRootSubDictFromFile(fileName)){ return; }
    if(!rootDict.stepIntoSubDictWithKey("metadata")){  return; }

    //Get an int value from the subdict
    int someInt = rootDict.getIntegerForKey("whatever the key is");

    //Step out of the sub dict and into another
    rootDict.stepOutOfSubDict();
    if(!rootDict.stepIntoSubDictWithKey("other dict")){  return; }

    //Read an array of strings from that dict
    vector strings = rootDict.getStringArrayForKey("key");

Any luck parsing through all that?

Played around with it today and everything is working beautifully so far =) Will upload the files in a sec, but almost no changes had to be made on my part.

What I ended up doing was that I changed the time consuming plist parsing to use DS_Dictionary. For instance I have a custom animation format for my characters that took around 5min to load on android, but now its as fast as iOS more/less =) Thank you!

DS_Dictionary.h https://dl.dropbox.com/u/7279678/DS_Dictionary/DS_Dictionary.h
DS_Dictionary.cpp https://dl.dropbox.com/u/7279678/DS_Dictionary/DS_Dictionary.cpp
DS_Dictionary_Extra.h https://dl.dropbox.com/u/7279678/DS_Dictionary/DS_Dictionary_Extra.h

Haven’t tried the array and CCRect things but they should work. Thank you for sharing =)

Glad to help

Is possible to get a dictionary into another dictionary, edit it, and save changes on plist???

Code is not commented and I don´t find that function on header.

Thanks for sharing your code :wink: