کدنگار

وبلاگ شخصی-آموزشی علی رشیدی

کدنگار

وبلاگ شخصی-آموزشی علی رشیدی

طبقه بندی موضوعی
پیوندهای روزانه
پیوندها

۵ مطلب در شهریور ۱۳۹۶ ثبت شده است

حدود یک ماه است که روی پروژه ای که بند بند وجودش به پردازش جیسون و محلی سازی است کار میکنم، تا حالا تجربه های کوچکی را با شما به اشتراک گذاشتم و حالا میخواهم در این مطلب، ارسال درخواست، ذخیره نتیجه، پردازش و محلی سازی جیسون را به وسیله اشیا توضیح بدهم.


ارسال درخواست و دریافت نتیجه

برای ارسال درخواست، تست شده ترین و ساده ترین روش به شرح زیر است:


  • ایجاد یک شی QNetworkAccessManager، QNetworkRequest و QNetworkReply
  • QNetworkAccessManager برای ارسال درخواست
  • QNetworkRequest درخواستی است که به شی قبل میدهیم.
  • QNetworkReply هم پاسخ یک درخواست را نگهداری میکند.
  • تنظیم مشخصات request و ارسال، برابر قرار دادن reply با شی بازگشت داده شده توسط request.
  • یک QEventLoop را اجرا میکنیم، این حلقه تا زمانی که اسلات quit آن صدا زده نشود برنامه را متوقف میکند.
  • سیگنال finished از request را به این اسلات وصل میکنیم، تا هنگامی که درخواست کامل شد (پاسخ کامل دریافت شد) از حلقه خارج شویم.
در نهایت ما یک شی از نوع QNetworkReply داریم که میتوانیم نتیجه درخواست را از آن بخوانیم.

//Files to include
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QObject>
#include <QUrl>

//Example code
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request;
QNetworkReply *reply;
QEventLoop loop;

connect(manager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));

request.setUrl(QUrl("Where we receive JSON from"));
reply = manager->get(request);
loop.exec();

پردازش جیسون

ایجاد سند

حالا که نتیجه درخواست را دارید (که قطعا یک جیسون معتبر باید باشد) میتوانید آنرا پردازش کنید. اولین قدم، ایجاد یک شی از نوع QJsonDocument است. برای اینکار از متد استاتیک fromJson این شی استفاده میکنیم.


نکته: متد استاتیک، متدی است که بدون اینکه یک شی ایجاد شده باشد قابل دسترسی است.


روش ایجاد یک سند با استفاده از نتیجه درخواست:

QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
if (doc.isNull())
qDebug() << "Unable to load JSON or invalid JSON document";

متد isNull بررسی میکند که سند ایجاد شده یا خیر. اگر ایجاد نشده باشد، ممکن است اصلا اطلاعات دریافت نشده باشد یا اینکه اطلاعات دریافتی دارای مشکل باشد.


کار با اشیا

من فرض را بر این میگیرم که ساختار جیسون را میدانید. کیوت برای اشیا جیسون کلاسی دارد به اسم QJsonObject. این کلاس میتواند یک شیئ را نگهداری کند.

اگر میخواهید راحت تر جیسون را پردازش کنید، بهتر است به صورت شی یا آرایه به آن دسترسی پیدا کنید. اگر میدانید ریشه سند شما یک شی است، به این صورت عمل کنید:

QJsonObject obj = doc.object();

توجه کنید که اگر ریشه سند آرایه باشد شی ایجاد شده تهی است.

حالا میخواهیم به یک کلید از سند دسترسی پیدا کنیم، اینکار با استفاده از براکت ها انجام میشود. مثلا اگر شی ما یک کلید به اسم name دارد به صورت زیر قابل دسترسی است:

obj["name"]

اما این داده نمیتواند به تنهایی کاری برای ما انجام دهد چرا که از نوع QJsonValue است. برای تبدیل آن به شی مناسب از متد های توضیح داده شده در مستندات کیوت، مربوط به QJsonValue استفاده کنید. مثلا ما میدانیم که نام یک رشته است، پس اینگونه آنرا مورد استفاده قرار میدهیم:

qDebug() << obj["name"].toString();
//Outputs "Ali"

کار با آرایه ها

برای دسترسی به هر عضو آرایه از متد at استفاده میکنیم. این متد هم یک QJsonValue برمیگرداند که کار تبدیل را راحت میکند.

مثلا اینجا یک عضو آرایه را به عنوان شی استفاده میکنیم:

/*Our JSON is:
[{"name":"Ali","age":18},{"name":"Sina","age":21}]
and is stored in arr
*/

QJsonObject aliObject = arr.at(0).toObject();
qDebug() << aliObject["name"];
//Outputs "Ali"

مثال بزرگتر

در مطلب بعدی یک مثال خواهم گذاشت که در آن خواندن جیسون از فایل، محلی سازی، نوشتن در فایل و ... انجام شده و برای مطالعه عالی میباشد.

  • علی رشیدی

تجربه ۳ رو یادتونه؟ که برای یه آرایه نامشخص انجام دادیم. یگ مشکل اساسی اون روش، این بود که برای یه آرایه دو بعدی یا بهتر بگم در کار من، آرایه ی آرایه، این روش بسیار پیچیده و بیشتر شبیه به ماست مالی میشد. و اینجا بود که به یاد دوست قدیمیمون وکتور افتادم. وکتور خیلی از مشکلات از قبیل سایز و ... رو نداره و خلاصه خیلی کارو راحت میکنه. میشه ریسایزش کرد و خلاصه خوراک کار من بود.


اما کاری که من میخواستم بکنم این بود: یک وکتورِ وکتور رو از روی یه آرایه ی آرایه که با جیسون دیکُد شده بسازم. روش این شد که یک وکتور وکتور به روش زیر بسازم، بعد بُعد اول رو بر اساس سایز بعد اول آرایه جیسون تغییر اندازه(ریسایز) کنم، و بعد برای هر بعد دوم، با استفاده از جیسون باز هم ریسایز کنم. در واقع یه چیزی شبیه به این:


    QVector<QVector<A>> myVect;
myVect.resize(2);
int i, j;

for (i = 0; i < 2; i++)
{
myVect[i].resize(4);
for (j = 0; j < 4; j++)
{
myVect[i][j] = A(i*j);
}
}

این روش، البته، یه جورایی فقط به درد من میخوره که سایز ها موقع تعریف وکتور برام مشخص نیست. اگر سایز وکتورتون مشخصه میتونید وکتور رو اینطوری تعریف کنید:


QVector<QVector<A>> myVect(3, QVector<A>(5));

در اینجا وکتورمون مثل یه آرایه هست که اینطوری تعریف شده باشه:


int arr[3][5];

تعریف کردن و ریسایز و ... در وکتور استاندارد هم کاملا مشابه همینه و اگه از اون استفاده میکنید کافیه QVector رو با vector جایگزین کنید.

  • علی رشیدی

در قسمتی دیگر از پروژه، این بار قرار بود که اطلاعات یک آرایه پویا (که از نوع یک شی بود) را بخوانم و پردازش کنم... خب، مشکل این بود که سایز آرایه نامعلوم بود و هنگام پردازش با خطا مواجه می شد. در واقع کلاسی که من داشتم:


class Test
{
public:
void print()
{
...
}

void setObject(MyObject *obj)
{
object = obj;
}

private:
MyObject *object;
};

خب، ست کردن آرایه که کاری نداشت اما مشکل در تابع print بود، چگونه بدون دانستن سایز آنرا چاپ کنیم؟ اول سرچ کردم و روش مشخصی پیدا نکردم، پس تصمیم گرفتم که ببینم اصلا اگر بخواهیم عضوی خارج از محدوده را بخوانیم چه میشود؟ در تابع پرینت عبارت


cout << object[4].getText();

را وارد کردم و این شی رو با یک آرایه ۴ عضوی ست کردم، و هنگام اجرا متوجه شدم که


terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc

این یک استثنا بود (Exception) و برای کنترل آن راحت می شد از try & catch استفاده کرد. پس پرینت را به این صورت نوشتم:


void print()
{
int i;
bool end;
try
{
i = 0;
while(!end)
{
cout << object[i].getText();
i++;
}
}

catch (const std::bad_alloc&)
{
end = true;
}
}

خیلی ساده، به محض اینکه به اولین عضو خارج از دامنه برسیم، استثنا رخ میدهد و خواندن ارایه متوقف میشود.

  • علی رشیدی
یه قسمت دیگه از پروژه، باید اطلاعات جیسون رو میگرفتم و در یک شی ذخیره میکردم. اما به یه مورد عجیب برخوردم: آرایه ارایه. خب اینا تقریبا همون آرایه دو بعدی خودمون هستن تو جیسون، ولی کاری که میخواستم بکنم این بود که یک شی به صورت ارایه دو بعدی رو با شی معادلش در جیسون Init کنم. و در نهایت پیاده سازیش شد اینی که میبینید. یک نمونه سادشو برای خودم انجام دادم با انواع استاندارد ++C تا بعدا توی پروژه اصلی پیاده سازی کنم.

  • علی رشیدی

پروژه جدیدم یک کتابخانه بود و من با وجود اینکه در مورد کتابخانه ها و ساخت آنها با ++g اطلاع داشتم، نمیدانستم که چگونه یک کتابخانه با کیوت بسازم. برای اینکه بفهمم خوب یاد گرفتم یه پروژه ساده انجام دادم که برای شما هم گذاشتمش D:

دانلود

این پروژه از یک کتابخانه و یک برنامه که از اون استفاده میکنه تشکیل شده. اینقد سادست که نیازی به توضیح دادن در موردش هم نمیبینم. البته توجه کنید که کتابخونه به صورت استاتیک ساخته میشه، اگه میخواهید که shared باشه خط 

CONFIG += static

رو حذف کنید تا کانفیگ پیش فرض یعنی shared در نظر گرفته شه.

  • علی رشیدی