ValueMap convert to data (for compression)

I’ve seen that there’s a function to create a ValueMap from data, but how to convert a ValueMap to data?

I want to save some data to file using ValueMap, but I’d like to save it compressed, not in the huge XML-format.

Also for sending the data over the Internet it would be way better to have it small.

What’s the best way to achieve that?

You have to call the appropriate conversion function on the value of the key/val pair you want.

myValueMap.at("myDataMember")->toint()

with myDataMember being the key.

There is a conversion function for every type.
E.g. toInt(), tofloat(),…

Well, I know that, but I mean how to convert the whole ValueMap to data at once.

It can be saved to file, so there must be an easy way to get the data without saving it to file.

I’m working with a large ValueMap which needs to be sent to another device.

There is no function to get the data, as you already have it: your ValueMap. You would need to do it the way I posted(to write out your own binary format) or use the XML parser and implement the compressing of the XML data yourself.
The ValueMap is just a std::unordered_map/std::map.

The writeToFile function is a function of FileUtils, which just uses the XML parser to write out the plist file.

/*
 * Use tinyxml2 to write plist files
 */
bool FileUtils::writeToFile(ValueMap& dict, const std::string &fullPath)
{
    //CCLOG("tinyxml2 Dictionary %d writeToFile %s", dict->_ID, fullPath.c_str());
    tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument();
    if (nullptr == doc)
        return false;
    
    tinyxml2::XMLDeclaration *declaration = doc->NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\"");
    if (nullptr == declaration)
    {
        delete doc;
        return false;
    }
    
    doc->LinkEndChild(declaration);
    tinyxml2::XMLElement *docType = doc->NewElement("!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
    doc->LinkEndChild(docType);
    
    tinyxml2::XMLElement *rootEle = doc->NewElement("plist");
    rootEle->SetAttribute("version", "1.0");
    if (nullptr == rootEle)
    {
        delete doc;
        return false;
    }
    doc->LinkEndChild(rootEle);
    
    tinyxml2::XMLElement *innerDict = generateElementForDict(dict, doc);
    if (nullptr == innerDict )
    {
        delete doc;
        return false;
    }
    rootEle->LinkEndChild(innerDict);
    
    bool ret = tinyxml2::XML_SUCCESS == doc->SaveFile(getSuitableFOpen(fullPath).c_str());
    
    delete doc;
    return ret;
}

You should look into a different serialization protocol like Cap’n Proto: https://capnproto.org/

Yes, I’ve seen the XML parser, but weirdly, it uses different code for android and iOS.

For iOS it uses the default iOS code (NSDictionary) and for android it uses tinyxml.
I think it would make sense to use tinyxml for both (if this is possible), so there’s one way to grab the result data.

Thanks, I’m going to look into other serialization formats that are more efficient.

It’s because the data is written as a plist. plists are native to all OS X variants, as these were invented by Apple/Next and are used for serialization of dictionary objects. They are implemented directly in the OS, so they are used on those platforms.
Android does not use plists.

That’s an argument, but would also introduce redundant code on the OS X platforms.

Cap’n Proto is one of the best solutions out there.

In cocos2d I know you can get ValueMap data from a file like a .plist with code like:

cocos2d::ValueMap _valMapToReadInFromPlist;

_valMapToReadInFromPlist = FileUtils::getInstance()->getValueMapFromFile("res/dataFile.plist");

cocos2d::ValueMap valMapDataWithinFile = _valMapToReadInFromPlist.at("keyInFile").asValueMap();

But to write to a file you can do it as a __Dictionary.

    __Dictionary* dictionaryToConvertToFile = __Dictionary::create();
    
    __Dictionary* dictionaryEntryWithinFile = __Dictionary::create();
    
    dictionaryEntryWithinFile->setObject(__Integer::create( 0 ), "randomIntSubData");
    
    dictionaryEntryWithinFile->setObject(__String::create( "randomStringData" ), "randomStringDataValue");
    
    dictionaryToConvertToFile->setObject(dictionaryEntryWithinFile, "keyForDictionaryData");
    
    dictionaryToConvertToFile->writeToFile( __String::createWithFormat("%s%s", cocos2d::FileUtils::getInstance()->getWritablePath().c_str() , "filenameForMyData.plist" )->getCString() );

The above code should create (or update) a file called

filenameForMyData.plist

with a dictionary row called

keyForDictionaryData

that drops down to have two sub-items: an Int row with key “randomIntSubData” and value “0” as well as a String row with key “randomStringSubData” and value “randomStringDataValue.” You should be able to read it as a ValueMap as specified at the beginning of this answer. I know it’s late, but hope this helps you or someone else! God bless you friend.

1 Like