به نام خدا،امروز و در این جلسه میخواهیم با Json و کاربرد اون در اندروید به همراه یک پروژه در خدمت دوستان باشیم.JSON که مخفف JavaScript Object Notation می باشد روشی نوشتاری برای انتقال داده مستقل می باشد ، که بسیار خوانا است و جایگزینی عالی برای XML محسوب می شود.

JSONیک قالب متنی است و به هیچ زبانی وابسته نیست و قالب آن برای برنامه نویسان خانواده C ( تمام زبان های برنامه نویسی که ساختار نوشتاری مشابه سی دارند ، مانند سی ، سی پلاس پلاس ، جاوا ، جاوا اسکریپت ، سی شارپ و ...) بسیار آشناست. انتخاب نام این فرمت به این خاطر است که از استاندارد های زبان برنامه نویسی جاوا اسکریپت گرفته شده است (توجه کنید که این فقط یک نام است و JSON یک جز از جاوا اسکریپت یا هیچ زبان نویسی دیگری نیست).
همینکه کار با JSON را یاد بگیرید برنامه های بسیاری جالبی می توانید بنویسید زیرا امروز سایت های بسیاری هستند که امکانات بسیار جالب و مفیدی را به صورت خروجی JSON در اختیار شما قرار می دهند.
در این آموزش یک برنامه ساده می نویسیم که از سایت openweathermap.org یک خروجی JSON دریافت می کند و وضعیت هوا ، رطوبت و سرعت باد شهر tehran را نمایش می دهد.(برای اینکار از http://api.openweathermap.org/data/2.5/weather?q=tehran استفاده می کنیم که در ادامه متوجه نحوه استفاده از این آدرس خواهید شد.).
بهترین روش برای آشنایی با مفاهیم مختلف استفاده از مثال است ، در اینجا نیز در قالب مثال با JSON و اینکه چگونه سایت ها امکانات خود را به صورت خروجی JSON در اختیار ما قرار می دهند آشنا می شویم.
اگر در مرورگر خود آدرس http://api.openweathermap.org/data/2.5/weather?q=tehran را تایپ کنید ممکنه یک خروجی نامرتبط رو بپنشون بده ولی جای نگرانی نیست با استفاده از سایت http://jsonprettyprint.com و وارد کردن خروجی آن را مرتبط مانند زیر به ما نشان دهد :
{
"coord": {
"lon": 51.42,
"lat": 35.69
},
"sys": {
"message": 0.005,
"country": "IR",
"sunrise": 1420688669,
"sunset": 1420724256
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "Sky is Clear",
"icon": "01n"
}
],
"base": "cmc stations",
"main": {
"temp": 274.234,
"temp_min": 274.234,
"temp_max": 274.234,
"pressure": 902.34,
"sea_level": 1030.36,
"grnd_level": 902.34,
"humidity": 74
},
"wind": {
"speed": 3.46,
"deg": 290.003
},
"clouds": {
"all": 0
},
"dt": 1420738673,
"id": 112931,
"name": "Tehran",
"cod": 200
}
object : هر عبارتی است که با علامت } شروع شده و با { خاتمه می یابد. همانطور که مشاهده می کنید خروجی فوق خود یک object است که در داخل آن object های دیگری وجود دارند.
array : آرایه در JSON با علامت ] شروع می شود و با علامت [ خاتمه می یابد.
key-value : جفت های کلید و مقدار در JSON با دو نقطه در وسط آن ها تعریف می شوند. در سمت چپ کلید (key) و در سمت راست مقدار (value ) قرار دارد.
سه مفهوم فوق به صورت تو در تو کنار هم قرار می گیرند و ساختار های بزرگ تر را ایجاد می کنند. به عنوان مثال کل خروجی قبلی یک object است زیرا با علامت } شروع می شود و با { خاتمه می یابد. در داخل این object عبارت های key-value بسیاری داریم که به عنوان مثال می توان به weather و main اشاره کرد. همانطور که مشاهده می کنید در جایی که weather کلید است مقدار یک آرایه است. و در مقابل main نیز یک object وجود دارد.
خب با JSON آشنا شدید ، اینکه سایت های مختلف چگونه خروجی JSON در اختیار ما قرار می دهند را نیز در قالب مثال دیدید پس چیزی نمانده ، تنها کاری که باید بکنیم به این صورت است :
- یک درخواست http از نوع GET یا POST ارسال کنیم (مثلاً http://api.openweathermap.org/data/2.5/weather?q=tehran در همین مثال)
- خروجی درخواست فرستاده شده را به صورت Stream دریافت کنیم.
- Stream دریافت شده را به String تبدیل کنیم.
- String آماده شده را تجزیه و تحلیل (Parse) کنیم.( این String در قالب JSON است و از ابزار های کار با JSON در اندروید استفاده خواهیم کرد.
ارسال درخواست http و دریافت خروجی به صورتStream
اینکار را به چند روش می توانیم انجام دهیم در اینجا تابع getStreamFromURL را به صورت زیر تعریف می کنیم که از این تابع به راحتی می توانید در پروژه های خودتان استفاده کنید :
InputStream getStreamFromURL(String urlString,String method){
try {
URL url=new URL(urlString);
HttpURLConnection huc=(HttpURLConnection)url.openConnection();
huc.setReadTimeout(10000);
huc.setConnectTimeout(15000);
huc.setRequestMethod(method);
huc.setDoInput(true);
huc.connect();
return huc.getInputStream();
} catch (Exception e) {
return null;
}
}
تبدیل Stream به String :
String streamToString(InputStream is){
String result="";
String line=null;
BufferedReader br=new BufferedReader(new InputStreamReader(is));
try {
while((line=br.readLine())!=null){
result+=line+"\n";
}
} catch (IOException e) {}
return result;
}
Parse کردن رشته JSON
در این برنامه می خواهیم به مقدار description از قسمت weather و مقدار speed از قسمت wind و مقدار humidity از قسمت main دسترسی پیدا کنیم. هر سه کلید main و wind و weather در داخل آبجکت اصلی قرار دارند برای دسترسی به مقادیر متناظر این کلید ها از getJSONObject استفاده می کنیم ، پس از به دست آوردن آبجکت مورد نظر از متد getString استفاده می کنیم و کلید را به آن ارسال می کنیم تا مقدار را به دست آوریم به رابطه قطعه کد زیر و عکس بعد از آن دقت کنید :
void parseJSON(String JSONString){
try {
JSONObject jo=new JSONObject(JSONString);
JSONArray weatherArray=jo.getJSONArray("weather");
JSONObject firstWeatherArrayObject=weatherArray.getJSONObject(0);
weatherResult=firstWeatherArrayObject.getString("description");
JSONObject windObject=jo.getJSONObject("wind");
windSpeedResult=windObject.getDouble("speed");
JSONObject mainObject=jo.getJSONObject("main");
humidityResult=mainObject.getInt("humidity");
} catch (JSONException e) {}
}
کل رشته JSON اصلی یک آبجکت است که در jo تعریف می شود.در مقابل کلید weather مقدار برابر آرایه است برای همین برای به دست آوردن این آرایه از فراخوانی getJSONArray روی jo استفاده می کنیم ، پس از به دست آوردن این آرایه اولین آبجکت این آرایه را با فرستادن 0 به getJSONObject بر روی weatherArray به دست می آوریم و آن را در firstWeatherArrayObject ذخیره می کنیم.
از متد getString برای دسترسی به مقادیری که از نوع رشته هستند استفاده می کنیم. از متد getDouble برای دسترسی به مقادیر از نوع اعداد اعشاری و از getInt برای دسترسی به مقادیر از نوع عدد صحیح استفاده می کنیم. کد فوق بسیار خواناست و نیازی به توضیح بیشتر نمی بینم.
در ادامه کار با یک برنامه اندروید ساده نحوه استفاده از JSON و ساخت یک برنامه هواشناسی بیشتر با مباحث توضیح داده شده آشنا می شویم.
برای شروع کار یک پروژه جدید ایجاد کنید
در داخل لایه اصلی برنامه activity_main از کدهای زیر استفاده می کنیم.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
tools:context=".MainActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tvWeather"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Weather:" />
<TextView
android:id="@+id/tvWeatherValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tvWindSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Wind Speed:" />
<TextView
android:id="@+id/tvWindSpeedValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tvHumidity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Humidity:" />
<TextView
android:id="@+id/tvHumidityValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/btnLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="Load" />
</RelativeLayout
کد اصلی کلاس MainActivity.java
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.*;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.app.Activity;
public class MainActivity extends Activity {
private String url1 = "http://api.openweathermap.org/data/2.5/weather?q=tehran";
Button load;
TextView weather,humidity,windSpeed;
String weatherResult;
double windSpeedResult;
int humidityResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
load=(Button)findViewById(R.id.btnLoad);
weather=(TextView)findViewById(R.id.tvWeatherValue);
humidity=(TextView)findViewById(R.id.tvHumidityValue);
windSpeed=(TextView)findViewById(R.id.tvWindSpeedValue);
load.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
ReadJSON rj=new ReadJSON();
rj.execute();
}
});
}
class ReadJSON extends AsyncTask<String, String, String> {
protected String doInBackground(String... args) {
InputStream jsonStream=getStreamFromURL(url1,"GET");
String jsonString=streamToString(jsonStream);
parseJSON(jsonString);
return null;
}
protected void onPostExecute(String file_url) {
weather.setText(weatherResult);
windSpeed.setText(""+windSpeedResult);
humidity.setText(""+humidityResult);
}
}
void parseJSON(String JSONString){
try {
JSONObject jo=new JSONObject(JSONString);
JSONArray weatherArray=jo.getJSONArray("weather");
JSONObject firstWeatherArrayObject=weatherArray.getJSONObject(0);
weatherResult=firstWeatherArrayObject.getString("description");
JSONObject windObject=jo.getJSONObject("wind");
windSpeedResult=windObject.getDouble("speed");
JSONObject mainObject=jo.getJSONObject("main");
humidityResult=mainObject.getInt("humidity");
} catch (JSONException e) {}
}
InputStream getStreamFromURL(String urlString,String method){
try {
URL url=new URL(urlString);
HttpURLConnection huc=(HttpURLConnection)url.openConnection();
huc.setReadTimeout(10000);
huc.setConnectTimeout(15000);
huc.setRequestMethod(method);
huc.setDoInput(true);
huc.connect();
return huc.getInputStream();
} catch (Exception e) {
return null;
}
}
String streamToString(InputStream is){
String result="";
String line=null;
try {
BufferedReader br=new BufferedReader(new InputStreamReader(is));
while((line=br.readLine())!=null){
result+=line+"\n";
}
} catch (Exception e) {}
return result;
}
}
چون عمل خواندن JSON عملی زمان بر است نباید در Thread اصلی برنامه صورت گیرد برای همین زیر کلاسی از AsyncTask ایجاد کردم(با AsyncTask در این آموزش آشنا شدیم) و متد های گفته شده را در داخل doInBackground آن فراخوانی کردم. پس از اتمام کار نتیجه توسط متد onPostExecute به UI برنامه منتقل می شود. توجه کنید که همانطور که در آموزش های قبلی نیز اشاره کردم متد onPostExecute به UI-Thread دسترسی دارد و نیازی نداریم که در داخل آن از runOnUiThread استفاده کنیم.
و در آخر هم دسترسی اینترنت در فایل AndroidManifest
<uses-permission android:name="android.permission.INTERNET"/>