Dostęp do Rails API z poziomu C++ [Część 2: Parsowanie formatu JSON]
W moim ostatnim wpisie stworzyłem prostą metodę GET. Teraz pokażę w jaki sposób przygotować Railsy do wysyłania odpowiedzi JSON po stronie serwera oraz w jaki sposób parsować je wewnątrz aplikacji C++.
ZACZYNAMY!
Mam nadzieję, że ostatnim razem pobraliście moją przykładową plikację z Github'a (jeżeli nie to zróbcie to szybko :). Należy pobrać kod aplikacji w C++ stąd oraz kod aplikacji RoR stąd. W tej części wykorzystamy drugi przykład z repozytorium C++.
Generowanie JSON w Railsach.
Zakomentujcie na chwilę całą metodę as_json z app/model/entry.rb (linie 3-14). Następnie uruchomcie lokalny serwer Ruby oraz przejdźcie na stronę 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"
}
Powyższy JSON został wygenerowany z użyciem domyślnej metody i pokazuje wszystkie pola danego modelu. W celu jego dostosowania należy nadpisać metodę as_json(options).
Ale jak wygenerować odpowiedź JSON? To naprawdę proste!
respond_to do |format|
format.html
format.json { render :json => @entry }
format.xml {render :xml => @entry}
end
Teraz zależności od sufiksu w adresie (.json, .xml albo brak dla htmla) Railsy dostarczą odpowiedź w odpowiednim formacie.
Przekazywanie plików binarnych wewnątrz JSON.
Jak mogliście zauważyć wcześniej domyślny JSON z Railsach przechowuje link do pliku w "plikowej" części odpowiedzi. To czego naprawdę potrzebujemy to umieszczenie całego pliku wewnątrz odpowiedzi! Niestety nie jest możliwe przechowywanie pliku binarnego wewnątrz JSON w czystej postaci. Z pomocą przychodzi tutaj kodowanie Base64, które pozwala na przekształcenie binarnego pliku w ciąg znaków. Ruby posiada wbudowaną metodę Base64.encode64 umozliwijącą zrobienie tego. Otworzenie pliku i przetworzenie go do Base64 za pomocą tej metody można wykonać za pomocą poniższego kodu:
def get_file_base64
Base64.encode64(open(file.current_path){ |io| io.read })
end
Żeby mieć pewność ,że po pobraniu naszego JSON'a otrzymamy własciwy obraz bez żadnych uszkodzonych danych powinniśmy porównać sumy kontrolne odebranego pliku z tym zawartym wewnątrz JSON'a. W celu stworzenia sumy kontrolnej i umieszczenia jej wewnątrz JSON'a możemy użyć algorytmu SHA-2.
def generate_file_checksum Digest::SHA256.hexdigest self.get_file_base64 end
I wreczcie parsowanie formatu JSON w C++
Do sparsowania tego w prosty sposób możemy użyć biblioteki jsoncpp.
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;
}
Jak widzicie parsowanie JSON'a z użyciem tej biblioteki jest naprawdę proste. Metoda ta zwraca prawdę jeżeli parsowanie się powiodło, a w innym razie fałsz.
Jedyną problematyczna częścią w powyższym kodzie jest wywołanie root.get(std::string, std::string). PIerwszy argument wskazuje na nazwę elementu JSON, drugi natomiast jest domyślną wartością. W sytuacji, gdy wartość elementu nie zostanie znaleziona funkcja użyje wartości domyślnej.
Oddzielną kwestią jest parsowanie dat.
updated_at = QDateTime::fromString(
QString::fromStdString(root.get("updated_at", "").asString()),
Qt::ISODate);
Dlatego użyjemy tutaj funkcji fromString ponieważ daty w Railsach generowane są domyślnie w formacie ISO_8601. Biblioteka Qt jest dobrze przygotowania do obsługi tego formatu - jedyne co musimy to ustawić Qt::ISODate jako używany format.
Parsowanie obrazków zapisanych w kodzie Base64
void 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);
}
Funkcja ta przyjmuje ciąg znaków zakodowany w Base64 oraz sumę kontrolną w naszego JSON'a. Zanim przekonwertujemy nasz ciąg znaków z powrotem do obrazka powinniśmy porównać sumy kontrolne. W celu wykonania tego możemy użyć biblioteki cryptopp.
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();
}
Należy pamiętać, że w celu prawidłowego porównania używamy funkcji .toUpper() albo .toLower() z racji tego, że ciąg generowany przez Cryptopp jest opisywany wielkimi znakami, natomiast ciąg generowany przez Railsy małymi znakami.
Dekodowanie obrazka
QImage JsonParser::decodeImage(const QString &encodedImage)
{
QByteArray imgData = QByteArray::fromBase64(encodedImage.toAscii());
QImage img;
img.loadFromData(imgData);
return img;
}
Jak widzicie możemy tutaj użyć QByteArray, która konwertuje kod Base64 z powrotem do postaci binarnego pliku. Mozemy również użyć QPixmap, niestety użycie QPixmap poza głównym wątkiem naszej aplikacji może prowadzić do problemów.
Podsumowanie
Mam nadzieję, że poznaliście sposób w jaki można uzyskać i sparsować format JSON w aplikacji C++. W następnej części zajmiemy się metodą POST.
BinarApps
Wpis został opublikowany dzięki współpracy z firmą BinarApps. Więcej wpisów o Ruby znajdziesz na ich blogu binarapps.com/blog
Szukasz szybkiego hostingu z dyskami SSD? Dobrze trafiłeś.
Pakiety hostingowe Kylos to sprawdzone i niezawodne rozwiązanie dla Twojej strony.
Darmowy okres próbny pozwoli Ci sprawdzić naszą ofertę, bez ponoszenia kosztów.
Sprawdź nas