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