In my last entry I created a simple get request. In this entry I will show how to prepare Rails to send server JSON responses and how to parse them inside our C++ application.

Let’s begin!

I hope that last time you have downloaded my example application from our github, if not – do it fast :). Grab the C++ application from here and the Rails app from here In this part we will use a second example form the C++ repo.

Generating JSON in rails

For a little moment please comment out the entire as_json method from app/models/entry.rb (lines 3-14). Run the local rails server and visit http://localhost:3000/entries/1.json

{
id: 1,
name: "sample entry",
text: "sample text",
file: 
{
url: "/uploads/entry/file/1/4728b84afe507895df20497b6e5b0c21.jpg"
},
created_at: "2014-07-04T12:48:52.954Z",
updated_at: "2014-07-04T12:48:52.954Z"
}

This JSON is generated using the default method and shows all the fields of given model. To customize we you need to override the as_json(options) method.

But how to generate JSON response? This is really simple!

respond_to do |format|
      format.html
      format.json { render :json => @entry }
      format.xml  {render :xml => @entry}
end

Now depending on the suffix in the address (.json.xml or empty for html) Rails will deliver a response in the proper format.

Passing binary files inside JSON

As you could see earlier the Rails default JSON holds a link to the file in the ‘file’ part of the response. But what we really need is to put the entire file in our response! Unfortunately it is impossible to store a binary file inside JSON ‘as is’, but here is where a handy coding system calledBase64 appears. To put it simply – this coding method allows us to transform binary data into text! Ruby has a built in method Base64.encode64 which allows us to do just that. To open a file and transform it into Base64 I used this method.

def generate_file_checksum
  Digest::SHA256.hexdigest self.get_file_base64
end

At last parsing JSON in C++

To parse it in a simple way we can use the jsoncpp library.

bool JsonParser::parseJson(const QString &pureJson)
{
    Json::Value root;
    Json::Reader reader;
    bool parsingSuccesful = reader.parse(pureJson.toStdString(), root);
    if(!parsingSuccesful)
        return false;
    entryId = root.get("id", -1).asInt();
    name = QString::fromStdString(root.get("name", "").asString());
    created_at = QDateTime::fromString(
                QString::fromStdString(root.get("created_at", "").asString()),
                Qt::ISODate);
    updated_at = QDateTime::fromString(
                QString::fromStdString(root.get("updated_at", "").asString()),
                Qt::ISODate);
    obtainImage(root["file"]);
    return true;
}

As you can see parsing JSON using this library is really simple. This method returns true if parsing was successful and false otherwise.
The only tricky part in the above code is calling root.get(std::string, std::string). The first argument shows the name of the JSON element, the second is the default value. In case that the element’s value is not found this function will use the default one.

Another thing is parsing dates

updated_at = QDateTime::fromString(
                QString::fromStdString(root.get("updated_at", "").asString()),
                Qt::ISODate);

For that, we’ll use the fromString function since dates from Rails are generated by default inISO_8601 format. Qt is well prepared to handle this, we only need to set Qt::ISODate as our desired format.

Parsing Base64 encoded images

oid JsonParser::obtainImage(const Json::Value &fileRoot)
{
    QString base64encoded = QString::fromStdString(fileRoot.get("base64encoded", "").asString());
    QString checksum = QString::fromStdString(fileRoot.get("checksum", "").asString());
    if(!compareChecksum(base64encoded, checksum))
        file = QImage();
    else
        file = decodeImage(base64encoded);
}

This function obtains the Base64 string and the file’s checksum from our json. Before we convert our encoded string back into an image, we should compare checksums, to do it we use thecryptopp library.

bool JsonParser::compareChecksum(const QString &encodedImage, const QString &orginal) const
{
    CryptoPP::SHA256 hash;
    byte digest[ CryptoPP::SHA256::DIGESTSIZE ];
    std::string message = encodedImage.toStdString();
    hash.CalculateDigest( digest, (byte *)message.c_str(), message.length() );
    CryptoPP::HexEncoder encoder;
    std::string output;
    encoder.Attach( new CryptoPP::StringSink( output ) );
    encoder.Put( digest, sizeof(digest) );
    encoder.MessageEnd();
    return QString::fromStdString(output).toUpper() == orginal.toUpper();
}

Keep in mind – to compare them properly use the .toUpper() or .toLower() functions since the cryptopp hash is represented as uppercase and the hash generated from Rails is lowercase.

Decoding our image

QImage JsonParser::decodeImage(const QString &encodedImage)
{
    QByteArray imgData = QByteArray::fromBase64(encodedImage.toAscii());
    QImage img;
    img.loadFromData(imgData);
    return img;
}

As you can we we can use QByteArray, which converts the Base64 encoded string back into binary data and then QImage load from data. We can also use QPixmap but unfortunately using QPIxmap outside our application’s main thread may lead to problems.

Conclusion

I hope you now know how to obtain and parse JSON in C++. In the next part I will show how to handle post requests.

Post tags:

Work
with us

Tell us about your idea
and we will find a way
to make it happen.

Get estimate

Join our awesome team
Check offers