Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
laboratoare:laborator07 [2015/04/19 16:46] Andrei Roșu-Cojocaru |
laboratoare:laborator07 [2015/04/22 17:34] (current) Andrei Roșu-Cojocaru |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Laborator 07. Comunicația prin HTTP ====== | + | ====== Laborator 07. Invocarea de Servicii Web prin Protocolul HTTP ====== |
| De multe ori, funcționalitatea pe care o pun la dispoziție aplicațiile Android este preluată din alte surse, datorită limitărilor impuse de capacitatea de procesare și memorie disponibilă ale unui dispozitiv mobil. O strategie posibilă în acest sens este utilizarea HTTP, pentru interogarea unor servicii web. | De multe ori, funcționalitatea pe care o pun la dispoziție aplicațiile Android este preluată din alte surse, datorită limitărilor impuse de capacitatea de procesare și memorie disponibilă ale unui dispozitiv mobil. O strategie posibilă în acest sens este utilizarea HTTP, pentru interogarea unor servicii web. | ||
| Line 321: | Line 321: | ||
| * utilizând un obiect de tip ''ResponseHandler'', ce furnizează conținutul resursei solicitate, transmis ca parametru al metodei ''execute()'' a clasei ''HttpClient'' (pe lângă obiectul ''HttpGet|HttpPost'') | * utilizând un obiect de tip ''ResponseHandler'', ce furnizează conținutul resursei solicitate, transmis ca parametru al metodei ''execute()'' a clasei ''HttpClient'' (pe lângă obiectul ''HttpGet|HttpPost'') | ||
| - | ===== Parsare DOM (Document Object Model) ===== | + | ===== Prelucrare DOM (Document Object Model) ===== |
| Documentele HTML au o structură arborescentă, având drept rădăcină un document al cărui copil este o ierarhie de elemente, fiecare dintre acestea fiind reprezentate de o etichetă. | Documentele HTML au o structură arborescentă, având drept rădăcină un document al cărui copil este o ierarhie de elemente, fiecare dintre acestea fiind reprezentate de o etichetă. | ||
| - | {{ :laboratoare:lab08-ierarhieelemente.jpg?nolink }} | + | {{ :laboratoare:laborator07:ierarhie_document_html.png?nolink&900 }} |
| - | Un element poate conține mai multe atribute (fiecare dintre acestea având o valoare asociată), respectiv o valoare.\\ | + | Fiecare element poate conține mai multe atribute (fiecare dintre acestea având o valoare asociată), putând încorpora și o valoare. |
| - | ''<TipElement atribut1="valoare1" ... atributn="valoaren"> valoare </TipElement>''\\ Nu este însă obligatoriu ca elementul să conțină nici atribute și nici valoare, putând avea și o structură care nu le include:\\ ''<TipElement />'' | + | |
| + | ''<TipElement atribut1="valoare1" ... atributn="valoaren"> valoare </TipElement>'' | ||
| - | Parsarea unui document HTML se poate realiza folosind biblioteca [[http://jsoup.org/packages/jsoup-1.8.2.jar|Jsoup]], care pune la dispoziție o metodă statică ''parse'' ce primește ca parametru conținutul resursei care se dorește a fi analizată. Rezultatul este un obiect [[http://jsoup.org/apidocs/org/jsoup/nodes/Document.html|Document]], care permite parcurgerea ierarhiei de elemente, prin intermediul metodei ''child'', ce primește ca parametru indexul pe care îl are copilul respectiv (numerotarea se face de la 0). La rândul său, aceasta returnează un obiect de tip [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html|Element]], ce oferă acces la toate proprietățile etichetei respective. | + | Nu este însă obligatoriu ca elementul să conțină nici atribute și nici valoare, putând avea și o structură care nu le include: |
| - | <note warning>Biblioteca ''jsoup-1.8.2.jar'' trebuie plasată în directorul ''libs'' al proiectului Android și adăugată la calea pentru compilare, în caz contrar putând fi generată o excepție de tip ''java.lang.NoClassDefFoundError'' la rularea aplicației. \\ În cazul în care eroarea se manifestă și în situația respectării acestor condiții, este necesară resetarea proprietăților proiectului Android: ''Project'' → ''Clean''.</note> | + | ''<TipElement />'' |
| + | |||
| + | O soluție pentru parsarea unui document HTML este reprezentată de biblioteca [[http://jsoup.org/packages/jsoup-1.8.2.jar|Jsoup]]. | ||
| + | |||
| + | <note important>Biblioteca ''jsoup-1.8.2.jar'' trebuie plasată în directorul ''libs'' al proiectului Android și adăugată la calea pentru compilare (//Build Path// → //Add to Build Path...//).</note> | ||
| + | |||
| + | Prin intermediul metodei statice [[http://jsoup.org/apidocs/org/jsoup/Jsoup.html#parse(java.lang.String)|parse()]] ce primește ca parametru conținutul resursei care se dorește a fi analizată (codul sursă al paginii HTML), se obține un obiect [[http://jsoup.org/apidocs/org/jsoup/nodes/Document.html|Document]], care permite parcurgerea ierarhiei de elemente. | ||
| + | |||
| + | Pentru un obiect de tipul ''org.jsoup.nodes.Document'', pot fi obținute secțiunile componente ''<head>'' și ''<meta>'' prin intermediul metodelor [[http://jsoup.org/apidocs/org/jsoup/nodes/Document.html#head()|head()]], respectiv [[http://jsoup.org/apidocs/org/jsoup/nodes/Document.html#body()|body()]]. | ||
| + | |||
| + | Fiecare etichetă din codul sursă corespunzător unei pagini HTML este reprezentat de un obiect [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html|Element]], prin intermediul căruia pot fi accesate toate atributele și valorile asociate acestora. | ||
| + | |||
| + | <note>Inclusiv nodul rădăcină (de tip ''org.jsoup.nodes.Document'') este derivat din ''org.jsoup.nodes.Element''.</note> | ||
| + | |||
| + | Pentru un element, pot fi realizate următoarele operații: | ||
| + | * obținerea (listei) unor elemente componente: | ||
| + | * deținând un anumit identificator - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementById(java.lang.String)|getElementById(String id)]]; | ||
| + | * care au o anumită etichetă - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByTag(java.lang.String)|getElementsByTag(String tag)]]; | ||
| + | * ce fac parte din cadrul unei clase - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByClass(java.lang.String)|getElementsByClass(String className)]]; | ||
| + | * în funcție de o proprietate: | ||
| + | * pentru care se precizează atributul | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttribute(java.lang.String)|getElementsByAttribute(String key)]]; | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeStarting(java.lang.String)|getElementsByAttributeStarting(String keyPrefix)]]; | ||
| + | * pentru care se precizează valoarea | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeValueStarting(java.lang.String, java.lang.String)|getElementsByAttributeValueStarting(String key, String valuePrefix)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeValueContaining(java.lang.String, java.lang.String)|getElementsByAttributeValueContaining(String key, String match)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeValueEnding(java.lang.String, java.lang.String)|getElementsByAttributeValueEnding(String key, String valueSuffix)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeValueMatching(java.lang.String, java.util.regex.Pattern)|getElementsByAttributeValueMatching(String key, Pattern pattern)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeValueMatching(java.lang.String, java.lang.String)|getElementsByAttributeValueMatching(String key, String regex)]]; | ||
| + | * pentru care se precizează atât atributul cât și valoarea - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#getElementsByAttributeValue(java.lang.String, java.lang.String)|getElementsByAttributeValue(String key, String value)]]. | ||
| + | * navigare în cadrul ierarhiei de elemente: | ||
| + | * obținerea elementului părinte: [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#parent()|parent()]]; în situația în care se dorește obținerea tuturor elementelor părinte (până la rădăcină), se poate folosi metoda [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#parents()|parents()]]; | ||
| + | * obținerea elementelor copii: [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#children()|children()]], [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#child(int)|child(int index)]] - numerotarea făcându-se în acest caz începând cu 0. | ||
| + | * obținerea elementelor de pe același nivel: [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#siblingElements()|siblingElements()]], [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#firstElementSibling()|firstElementSibling()]], [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#lastElementSibling()|lastElementSibling()]], [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#nextElementSibling()|nextElementSibling()]], [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#previousElementSibling()|previousElementSibling()]]; | ||
| + | * investigarea unor informații cu privire la proprietăți: | ||
| + | * identificatorul - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#id()|id()]]; | ||
| + | * eticheta - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#tag()|tag()]], [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#tagName()|tagName()]]; | ||
| + | * clasa / clasele - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#className()|className()]] / [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#classNames()|classNames()]]; | ||
| + | * atribut: | ||
| + | * lista tuturor atributelor - [[http://jsoup.org/apidocs/org/jsoup/nodes/TextNode.html#attributes()|attributes()]]; | ||
| + | * valoarea asociată unui anumit atribut - [[http://jsoup.org/apidocs/org/jsoup/select/Elements.html#attr(java.lang.String)|attribute(String attributeKey)]]; | ||
| + | * textul conținut, din care sunt eliminate caracterele albe, acesta fiind totodată normalizat: [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#text()|text()]]; | ||
| + | * datele, pentru etichetele ''<script>'', respectiv ''<style>'' - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#data()|data()]]. | ||
| + | * actualizarea codului sursă corespunzător paginii HTML: | ||
| + | * înainte de element: | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#prepend(java.lang.String)|prepend(String html)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#prependText(java.lang.String)|prependText(String text)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#prependElement(java.lang.String)|prependElement(String tagName)]] | ||
| + | * după element: | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#append(java.lang.String)|append(String html)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#appendText(java.lang.String)|appendText(String text)]] | ||
| + | * [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#appendElement(java.lang.String)|appendElement(String tagName)]] | ||
| + | * lista copiilor unui element: | ||
| + | * înainte - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#prependChild(org.jsoup.nodes.Node)|prependChild(Node child)]] | ||
| + | * după - [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#appendChild(org.jsoup.nodes.Node)|appendChild(Node child)]] | ||
| + | * prin specificarea conținutului său [[http://jsoup.org/apidocs/org/jsoup/nodes/Element.html#html(java.lang.String)|html(String html)]] | ||
| + | |||
| + | Unele dintre metode ce furnizează liste de elemente întorc rezultate de tipul [[http://jsoup.org/apidocs/org/jsoup/select/Elements.html|Elements]], care nu este altceva decât un vector de elemente (fiind derivat din ''ArrayList<Element>''), ce poate fi iterat (prin intermediul metodei [[http://docs.oracle.com/javase/1.5.0/docs/api/java/util/List.html#iterator()|iterator()]]). De asemenea, pot fi obținute referințe către primul, respectiv ultimul element din listă prin intermediul metodelor [[http://jsoup.org/apidocs/org/jsoup/select/Elements.html#first()|first()]], respectiv [[http://jsoup.org/apidocs/org/jsoup/select/Elements.html#last()|last()]]. | ||
| + | |||
| + | ==== Exemplu ==== | ||
| + | |||
| + | Se dorește listarea disciplinelor pentru care materialele didactice sunt disponibile pe platforma [[http://ocw.cs.pub.ro/courses|Open CourseWare]], vizualizându-se, pentru fiecare dintre acestea, pictograma asociată și denumirea disciplinei de învățământ. | ||
| + | |||
| + | În acest sens, se obține codul sursă al paginii HTML corespunzător, inspectându-se elementele care conțin informațiile care se doresc a fi afișate: | ||
| + | * adresa la care este stocată pictograma fiecărei discipline este conținută într-un element ''<img>'' care are specific atributul ''class'' având valoarea ''mediaright'', accesarea ei putând fi realizată prin intermediul proprietății ''src''; | ||
| + | * denumirea cursului este afișată folosind caractere îngroșate, fiind încapsulată în cadrul elementului ''<strong>'' ... ''</strong>''. | ||
| + | |||
| + | Conținutul paginii Internet, sub forma unui obiect ''org.jsoup.nodes.Element'', se obține prin apelul metodei statice ''parse()'' din clasa ''org.jsoup.Jsoup'' pe șirul de caractere ce conține codul sursă, după care elementul rădăcină (''<html>'') este accesat ca fiind unicul descendent direct: <code java> | ||
| + | Element htmlTag = Jsoup.parse(pageSourceCode).child(0); | ||
| + | </code> | ||
| + | |||
| + | Lista tuturor elementelor care conțin adresele la care sunt găzduite pictogramele disciplinelor de învâțământ se obține printr-o filtrare, dorindu-se reținerea acelor etichete pentru care atributul ''class'' are valoarea ''mediaright''. Colecția astfel generată este iterată element cu element, obținându-se apoi valoarea atributului ''src''. După ce se cunoaște adresa Internet la care se găsește imaginea respectivă, se descarcă fluxul de intrare asociat, acesta fiind decodificat într-un obiect ''Bitmap'' (după ce sunt realizate și alte operații, cum ar fi scalarea și aplicarea transparenței). | ||
| <code java> | <code java> | ||
| - | // ... | + | Elements imgTagsClassMediaright = htmlTag.getElementsByAttributeValue(Constants.CLASS_ATTRIBUTE, Constants.MEDIARIGHT_VALUE); |
| - | Document document = Jsoup.parse(result); | + | Iterator<Element> logoIterator = imgTagsClassMediaright.iterator(); |
| + | while (logoIterator.hasNext()) { | ||
| + | Element imgTagClassMediaRight = logoIterator.next(); | ||
| + | String logoAddress = Constants.OCW_BASE_INTERNET_ADDRESS + imgTagClassMediaRight.attr(Constants.SRC_ATTRIBUTE); | ||
| + | // ... | ||
| + | } | ||
| + | </code> | ||
| - | Element root = document.child(0); // <html> | + | Lista tuturor denumirilor aferente obiectelor de studiu poate fi accesată tot printr-o filtrare, menținându-se doar etichetele de tipul ''<strong>'' ... ''</strong>''. Pentru fiecare dintre acestea se preia textul propriu pe care îl conțin. |
| - | Element firstChild = root.child(0); | + | <code java> |
| - | // .. | + | Elements strongTags = htmlTag.getElementsByTag(Constants.STRONG_TAG); |
| - | Element nthChild = root.child(n-1); | + | Iterator<Element> nameIterator = strongTags.iterator(); |
| + | while (nameIterator.hasNext()) { | ||
| + | Element strongTag = nameIterator.next(); | ||
| + | String name = strongTag.ownText(); | ||
| + | // ... | ||
| + | } | ||
| + | </code> | ||
| - | Element firstGrandChild1 = firstChild.child(0); // ... | + | Informațiile astfel obținute vor fi stocate într-un obiect de tip adaptor asociat unei liste în cadrul căreia vor fi afișate. |
| - | Element nthGrandChild1 = nthChild.child(0); // ... | + | |
| - | println(someChild.text()); | + | Ca în cazul altor operații care implică procesări prin rețea și actualizarea controalelor grafice pe baza datelor care au fost procesate în acest mod, trebuie respectate următoarele reguli: |
| + | * comunicația prin rețea se face pe un fir de execuție separat; | ||
| + | * accesul la elementele din interfața grafică este permisă numai din contextul firului de execuție dedicat, cel care este proprietarul acestora. | ||
| + | |||
| + | <code java> | ||
| + | private class OcwCoursesDisplayerThread extends Thread { | ||
| + | @Override | ||
| + | public void run() { | ||
| + | ArrayList<OcwCourseInformation> ocwCourseInformationList = new ArrayList<OcwCourseInformation>(); | ||
| + | |||
| + | HttpClient httpClient = new DefaultHttpClient(); | ||
| + | HttpGet httpWebPageGet = new HttpGet(Constants.OCW_BASE_INTERNET_ADDRESS + Constants.OCW_REFERRENCE_INTERNET_ADDRESS); | ||
| + | ResponseHandler<String> responseHandler = new BasicResponseHandler(); | ||
| + | try { | ||
| + | String pageSourceCode = httpClient.execute(httpWebPageGet, responseHandler); | ||
| + | Document document = Jsoup.parse(pageSourceCode); | ||
| + | Element htmlTag = document.child(0); | ||
| + | Elements imgTagsClassMediaright = htmlTag.getElementsByAttributeValue(Constants.CLASS_ATTRIBUTE, Constants.MEDIARIGHT_VALUE); | ||
| + | Iterator<Element> logoIterator = imgTagsClassMediaright.iterator(); | ||
| + | while (logoIterator.hasNext()) { | ||
| + | Element imgTagClassMediaRight = logoIterator.next(); | ||
| + | String logoAddress = Constants.OCW_BASE_INTERNET_ADDRESS + imgTagClassMediaRight.attr(Constants.SRC_ATTRIBUTE); | ||
| + | HttpGet httpLogoGet = new HttpGet(logoAddress); | ||
| + | HttpResponse httpResponse = httpClient.execute(httpLogoGet); | ||
| + | HttpEntity httpEntity = httpResponse.getEntity(); | ||
| + | |||
| + | OcwCourseInformation ocwCourseInformation = new OcwCourseInformation(); | ||
| + | Bitmap logo = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(httpEntity.getContent()), | ||
| + | Constants.LOGO_WIDTH, | ||
| + | Constants.LOGO_HEIGHT, | ||
| + | false); | ||
| + | ocwCourseInformation.setLogo(Utilities.makeTransparent(logo)); | ||
| + | ocwCourseInformationList.add(ocwCourseInformation); | ||
| + | } | ||
| + | Elements strongTags = htmlTag.getElementsByTag(Constants.STRONG_TAG); | ||
| + | Iterator<Element> nameIterator = strongTags.iterator(); | ||
| + | int currentPosition = 0; | ||
| + | while (nameIterator.hasNext()) { | ||
| + | Element strongTag = nameIterator.next(); | ||
| + | String name = strongTag.ownText(); | ||
| + | |||
| + | ocwCourseInformationList.get(currentPosition++).setName(name); | ||
| + | } | ||
| + | |||
| + | final OcwCourseInformationAdapter ocwCourseInformationAdapter = new OcwCourseInformationAdapter(OcwCoursesDisplayerActivity.this, ocwCourseInformationList); | ||
| + | ocwCoursesListView.post(new Runnable() { | ||
| + | @Override | ||
| + | public void run() { | ||
| + | ocwCoursesListView.setAdapter(ocwCourseInformationAdapter); | ||
| + | } | ||
| + | }); | ||
| + | } catch (ClientProtocolException clientProtocolException) { | ||
| + | Log.e(Constants.TAG, clientProtocolException.getMessage()); | ||
| + | if (Constants.DEBUG) { | ||
| + | clientProtocolException.printStackTrace(); | ||
| + | } | ||
| + | } catch (IOException ioException) { | ||
| + | Log.e(Constants.TAG, ioException.getMessage()); | ||
| + | if (Constants.DEBUG) { | ||
| + | ioException.printStackTrace(); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | } | ||
| </code> | </code> | ||
| - | Obiectul de tip ''Element'' oferă posibilitatea filtrării elementelor din cadrul ierarhiei pe care o conține pe baza denumirii etichetelor respective, a atributelor și a valorilor acestora, respectiv a valorii proprii. Metodele în cauză au denumirea ''getElementsBy...'' și au drept rezultat un obiect [[http://jsoup.org/apidocs/org/jsoup/select/Elements.html|Elements]], care nu este altceva decât un vector de elemente, ce poate fi iterat. | + | {{ :laboratoare:laborator07:ocwcoursesdisplayer.png?nolink&400 }} |
| + | |||
| + | ===== Prelucrare JSON (JavaScript Object Notation) - opțional ===== | ||
| + | |||
| + | Unele servicii web folosesc [[http://en.wikipedia.org/wiki/JSON|formatul JSON]] pentru transferul de date întrucât, spre diferență de XML care implică numeroase informații suplimentare, acesta optimizează cantitatea de date implicate. De asemenea, este foarte ușor pentru un utilizator uman să prelucreze informațiile reprezentate într-un astfel de format. | ||
| + | |||
| + | Acest mecanism de reprezentare a datelor a fost utilizat inițial în JavaScript, unde acesta descria literali sau obiecte anonime, folosite o singură dată (fără posibilitatea de a fi reutilizate), informațiile fiind transmise prin intermediul unor șiruri de caractere. Ulterior, a fost preliat pe scară largă. | ||
| + | |||
| + | În principiu, informațiile reprezentate în format JSON au structura unei colecții de perechi de tip (cheie, valoare), fiecare dintre acestea fiind grupate într-o listă de obiecte ordonate. Nu se folosesc denumiri de etichete, utilizându-se doar caracterele ''"'', '','', ''{'', ''}'', ''['' și '']''. | ||
| + | |||
| + | <code json> | ||
| + | [ | ||
| + | { | ||
| + | "attribute1": "value11", | ||
| + | "attribute2": "value12", | ||
| + | ..., | ||
| + | "attributen": "value1n" | ||
| + | }, | ||
| + | { | ||
| + | "attribute1": "value21", | ||
| + | "attribute2": "value22", | ||
| + | ..., | ||
| + | "attributen": "value2n" | ||
| + | }, | ||
| + | ... | ||
| + | { | ||
| + | "attribute1": "valuem1", | ||
| + | "attribute2": "valuem2", | ||
| + | ..., | ||
| + | "attributen": "valuemn" | ||
| + | } | ||
| + | ] | ||
| + | </code> | ||
| + | |||
| + | În Android este suportat formatul JSON strict, așa cum este descris pe [[http://json.org/|]]: | ||
| + | * denumirile de atribute trebuie să se găsească între ghilimele; | ||
| + | * valorile de tip șir de caractere trebuie să se găsească între ghilimele (nu sunt permise apostroafele în acest caz). | ||
| + | |||
| + | Potrivit [[https://www.ietf.org/rfc/rfc4627.txt|RFC 4627]], tipul MIME al documentelor reprezentate în format JSON treuie să fie în mod necesar ''application/json''. | ||
| + | |||
| + | Există [[http://www.programmableweb.com/category/all/apis?data_format=21173|numeroase servicii web]] care își expun funcționalitatea prin intermediul unor documente JSON: | ||
| + | * [[https://developers.google.com/custom-search/json-api/v1/overview?csw=1|Google]]; | ||
| + | * [[https://developer.yahoo.com/|Yahoo]]; | ||
| + | * [[http://www.geonames.org/export/web-services.html|Geonames]]; | ||
| + | * [[https://dev.twitter.com/|Twitter]]; | ||
| + | * [[https://www.flickr.com/services/api/|Flickr]]. | ||
| + | |||
| + | În Android, prelucrarea documentelor reprezentate în format JSON este realizată prin intermediul clasei [[http://developer.android.com/reference/org/json/JSONObject.html|JSONObject]] care a fost integrată parțial (cu unele funcționalități lipsă), fără a se specifica clar versiunea care este utilizată. | ||
| + | |||
| + | Un document JSON poate fi construit în mai multe moduri: | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#JSONObject%28%29|JSONObject()]] - construiește un obiect JSON vid, care nu conține nici o asociere (atribut, valoare); | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#JSONObject%28java.util.Map%29|JSONObject(Map copyFrom)]] - contruiește un obiect JSON pe baza unor asocieri prealabile; o astfel de metodă este utilizată mai ales în situația în care se dorește să se transmită informații către un serviciu web; | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#JSONObject%28java.lang.String%29|JSONObject(String json)]] - construiește un obiect JSON prin parsarea unui șir de caractere; o astfel de metodă este utilizată mai ales în situația în care sunt primite informații de la un serviciu web, care trebuie să fie prelucrate; | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#JSONObject%28org.json.JSONTokener%29|JSONObject(JSONTokener readFrom)]] - construiește un obiect JSON pe baza valorii următoare disponibile în cadrul unui obiect [[http://developer.android.com/reference/org/json/JSONTokener.html|JSONTokener]]; | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#JSONObject%28org.json.JSONObject,%20java.lang.String[]%29|JSONObject(JSONObject copyFrom, String[] names)]] - construiește un obiect JSON prin filtrarea asocierilor existente într-un obiect ''JSONObject'', pe baza unor denumiri de atribute existente. | ||
| + | |||
| + | De regulă, metodele care operează cu obiecte JSON pot genera excepția [[http://developer.android.com/reference/org/json/JSONException.html|JSONException]] în situația în care sunt întâlnite documente care nu sunt bine formate. | ||
| + | |||
| + | <note warning>În Android nu este implementată metoda ''JSONObject(Bean bean)'' care permite construirea unui obiect JSON pe baza unei componente Java Bean.</note> | ||
| + | |||
| + | Gestiunea obiectelor JSON se face: | ||
| + | * pentru citire, prin intermediul metodelor de tip [[http://developer.android.com/reference/org/json/JSONObject.html#get%28java.lang.String%29|get(String name)]], care furnizează un obiect generic (de tip ''Object''), aceasta fiind supraîncărcată și pentru tipurile ''boolean'', ''double'', ''int'', ''long'', ''String'', ''JSONObject'', ''JSONArray''; | ||
| + | * pentru scriere, prin intermediul metodelor de tip [[http://developer.android.com/reference/org/json/JSONObject.html#put%28java.lang.String,%20java.lang.Object%29|put(String name, Object value)]], care furnizeză un rezultat de tip ''JSONObject'', existând și implementări specifice pentru tipurile ''boolean'', ''double'', ''int'', ''long''. | ||
| + | |||
| + | Verificarea faptului că un document JSON deține un anumit atribut se face prin invocarea metodei [[http://developer.android.com/reference/org/json/JSONObject.html#has%28java.lang.String%29|has(String name)]]. Funcționalitatea inversă este furnizată de metoda [[http://developer.android.com/reference/org/json/JSONObject.html#isNull%28java.lang.String%29|isNull(String name)]], aceasta verificând și dacă valoarea asociată atributului respectiv este ''null''. | ||
| + | |||
| + | Dimensiunea unui obiect ''JSONObject'', reprezentat sub forma numărului de asocieri de tip (cheie, valoare) este furnizat de metoda [[http://developer.android.com/reference/org/json/JSONObject.html#length%28%29|length()]]. | ||
| + | |||
| + | Reprezentarea unui obiect JSON sub forma unui șir de caractere poate fi obținută prin intermediul metodelor: | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#toString%28%29|toString()]] - formă compactă; | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#toString%28%29|toString(int indentSpaces)]] - formă utilizată în scop de depanare. | ||
| + | |||
| + | Unele metode furnizează vectori de obiecte JSON, reprezentate de clasa [[http://developer.android.com/reference/org/json/JSONArray.html|JSONArray]], în care accesul la obiecte se face indexat, începând cu 0: | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#names%28%29|names()]] - lista numelor dintr-un obiect JSON; | ||
| + | * [[http://developer.android.com/reference/org/json/JSONObject.html#toJSONArray%28org.json.JSONArray%29|toJSONArray(JSONArray names)]] - lista valorilor asociate unor atribute dintr-un vector de obiecte JSON. | ||
| + | |||
| + | ==== Exemplu ==== | ||
| + | |||
| + | Se dorește să se implementeze o aplicație Android care să afișeze informații cu privire la cutremurele care s-au produs într-un anumit areal geografic (desemnat prin coordonatele nord, sud, est, vest) pe baza rezultatelor furnizate de serviciul web [[http://api.geonames.org/earthquakesJSON|Geonames Earthquakes]]. | ||
| + | |||
| + | Detaliile care se doresc a fi vizualizate pentru fiecare cutremur în parte sunt: | ||
| + | * așezarea geografică (latitudinea și longitudinea); | ||
| + | * magnitudinea; | ||
| + | * adâncimea la care a avut loc; | ||
| + | * sursa informației; | ||
| + | * data și ora la care s-a înregistrat. | ||
| + | |||
| + | {{ :laboratoare:laborator07:earthquakelister.png?nolink&400 }} | ||
| + | |||
| + | Adresa Internet la care este disponibil rezultatul în format JSON trebuie să specifice în mod necesar informațiile cu privire la coordonatele punctelor cardinale, precum și cu privire la numele de utilizator: | ||
| + | * ''north'', ''south'', ''east'', ''west'' - valoarea coordonatelor corespunzătoare punctelor cardinale, exprimate ca numere reale, cu semn; | ||
| + | * ''username'' - denumirea utilizatorului care deține drepturi de acces asupra acestui serviciu web (în cazul de față, ''pdsd''). | ||
| + | |||
| + | **Exemplu**. O adresă Internet de la care pot fi accesate rezultate în format JSON este: [[http://api.geonames.org/earthquakesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&username=pdsd|]]. | ||
| + | |||
| + | Rezultatele sunt furnizate în următorul format: | ||
| + | |||
| + | <code json> | ||
| + | { | ||
| + | "earthquakes":[ | ||
| + | { | ||
| + | "eqid":"c0001xgp", | ||
| + | "magnitude":8.8, | ||
| + | "lng":142.369, | ||
| + | "src":"us", | ||
| + | "datetime":"2011-03-11 04:46:23", | ||
| + | "depth":24.4, | ||
| + | "lat":38.322 | ||
| + | }, | ||
| + | { | ||
| + | "eqid":"c000905e", | ||
| + | "magnitude":8.6, | ||
| + | "lng":93.0632, | ||
| + | "src":"us", | ||
| + | "datetime":"2012-04-11 06:38:37", | ||
| + | "depth":22.9, | ||
| + | "lat":2.311 | ||
| + | }, | ||
| + | ... | ||
| + | ] | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Astfel, informațiile care trebuie preluate din rezultat sunt: | ||
| + | ^ atribut JSON ^ tip de date ^ detaliu ^ | ||
| + | | ''lat'' | ''double'' | latitudine | | ||
| + | | ''lng'' | ''double'' | longitudine | | ||
| + | | ''magnitude'' | ''double'' | magnitudinea | | ||
| + | | ''depth'' | ''double'' | adâncimea | | ||
| + | | ''src'' | ''String'' | sursa informației | | ||
| + | | ''datetime'' | ''String'' | data și ora la care s-au înregistrat datele | | ||
| + | |||
| + | Prin urmare, pașii care trebuie urmați pentru implementarea funcționalității sunt: | ||
| + | - construirea URL-ului, prin atașarea informațiilor preluate de la utilizator (coordonatele punctelor cardinale); | ||
| + | - execuția unei cereri HTTP; | ||
| + | - parsarea documentului JSON corespunzător răspunsului HTTP; <code java> | ||
| + | final ArrayList<EarthquakeInformation> earthquakeInformationList = new ArrayList<EarthquakeInformation>(); | ||
| + | JSONObject result = new JSONObject(content); | ||
| + | JSONArray jsonArray = result.getJSONArray(Constants.EARTHQUAKES); | ||
| + | for (int k = 0; k < jsonArray.length(); k++) { | ||
| + | JSONObject jsonObject = jsonArray.getJSONObject(k); | ||
| + | earthquakeInformationList.add(new EarthquakeInformation( | ||
| + | jsonObject.getDouble(Constants.LATITUDE), | ||
| + | jsonObject.getDouble(Constants.LONGITUDE), | ||
| + | jsonObject.getDouble(Constants.MAGNITUDE), | ||
| + | jsonObject.getDouble(Constants.DEPTH), | ||
| + | jsonObject.getString(Constants.SOURCE), | ||
| + | jsonObject.getString(Constants.DATETIME) | ||
| + | )); | ||
| + | } | ||
| + | </code> | ||
| + | - atașarea informațiilor obținute în cadrul unui control grafic de tip listă. | ||
| ===== Activitate de Laborator ===== | ===== Activitate de Laborator ===== | ||
| Line 386: | Line 683: | ||
| <code> | <code> | ||
| operation = addition|substraction|multiplication|division | operation = addition|substraction|multiplication|division | ||
| - | operation1 = număr real | + | operator1 = număr real |
| - | operation2 = număr real | + | operator2 = număr real |
| </code> | </code> | ||
| Line 418: | Line 715: | ||
| **5.** Să se importe în mediul integrat de dezvoltare Eclipse Luna SR1a (4.4.1) proiectul ''02-XkcdCartoonDisplayer'' din directorul ''labtasks''. | **5.** Să se importe în mediul integrat de dezvoltare Eclipse Luna SR1a (4.4.1) proiectul ''02-XkcdCartoonDisplayer'' din directorul ''labtasks''. | ||
| - | Se cere să se implementeze o aplicație Android care afișează, într-un control grafic de tip ''ImageView'', cea mai recentă caricatură de pe pagina Internet [[http://www.xkcd.com|xkcd]]. De asemenea, va putea fi vizualizat titlul acestei carticaturi, adresa Internet la care aceasta poate fi accesată precum și butoane de legătură către caricatura precedentă, respectiv caricatura următoare (în situația în care aceasta există). | + | Se cere să se implementeze o aplicație Android care afișează, într-un control grafic de tip ''ImageView'', cea mai recentă caricatură de pe pagina Internet [[http://www.xkcd.com|xkcd]]. De asemenea, va putea fi vizualizat titlul acestei caricaturi, adresa Internet la care aceasta poate fi accesată precum și butoane de legătură către caricatura precedentă, respectiv caricatura următoare (în situația în care aceasta există). |
| {{ :laboratoare:laborator07:xkcdcartoondisplayer.png?nolink&400 }} | {{ :laboratoare:laborator07:xkcdcartoondisplayer.png?nolink&400 }} | ||
| Line 504: | Line 801: | ||
| **Exemplu** | **Exemplu** | ||
| + | |||
| În situația în care se dorește să se caute după șirul de caractere ''"android developers"'', adresa Internet la care se găsesc rezultatele căutării este: http://www.google.ro/search?q=android+developers. | În situația în care se dorește să se caute după șirul de caractere ''"android developers"'', adresa Internet la care se găsesc rezultatele căutării este: http://www.google.ro/search?q=android+developers. | ||
| Line 517: | Line 815: | ||
| </code> | </code> | ||
| - | **7.** Să se încarce modificările realizate în cadrul depozitului 'Laborator07' de pe contul Github personal, folosind un mesaj sugestiv. <code> | + | **7.** (opțional) Să se importe în mediul integrat de dezvoltare Eclipse Luna SR1a (4.4.1) proiectul ''04-LandmarkLister'' din directorul ''labtasks''. |
| + | |||
| + | Se cere să se implementeze o aplicație Android care să afișeze informații cu privire la obiectivele turistice dintr-un areal geografic (desemnat prin coordonatele nord, sud, est, vest) pe baza rezultatelor furnizate de serviciul web [[http://http://api.geonames.org/citiesJSON|Geonames Paces]]. | ||
| + | |||
| + | Detaliile care se doresc a fi vizualizate pentru fiecare obiectiv turistic în parte sunt: | ||
| + | * așezarea geografică (latitudinea și longitudinea); | ||
| + | * denumirea toponimului (de regulă, localitatea în care se găsește); | ||
| + | * populația; | ||
| + | * descrierea localității în care poate fi vizitat; | ||
| + | * numele obiectivului; | ||
| + | * adresa paginii Internet la care pot fi consultate informații suplimentare, pe Wikipedia; | ||
| + | * codul de țară. | ||
| + | |||
| + | {{ :laboratoare:laborator07:landmarklister.png?nolink&400 }} | ||
| + | |||
| + | Adresa Internet la care este disponibil rezultatul în format JSON conține informații cu privire la coordonatele punctelor cardinale, precum și cu privire la numele de utilizator: | ||
| + | * ''north'', ''south'', ''east'', ''west'' - valoarea coordonatelor corespunzătoare punctelor cardinale, exprimate ca numere reale, cu semn; | ||
| + | * ''username'' - denumirea utilizatorului care deține drepturi de acces asupra acestui serviciu web (în cazul de față, ''pdsd''). | ||
| + | |||
| + | **Exemplu**. O adresă Internet de la care pot fi accesate rezultate în format JSON este: [[http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&username=pdsd|]]. | ||
| + | |||
| + | Rezultatele sunt furnizate în următorul format: | ||
| + | |||
| + | <code json> | ||
| + | { | ||
| + | "geonames":[ | ||
| + | { | ||
| + | "fcodeName":"capital of a political entity", | ||
| + | "toponymName":"Mexico City", | ||
| + | "countrycode":"MX", | ||
| + | "fcl":"P", | ||
| + | "fclName":"city, village,...", | ||
| + | "name":"Mexiko-Stadt", | ||
| + | "wikipedia":"en.wikipedia.org/wiki/Mexico_City", | ||
| + | "lng":-99.12766456604, | ||
| + | "fcode":"PPLC", | ||
| + | "geonameId":3530597, | ||
| + | "lat":19.428472427036, | ||
| + | "population":12294193 | ||
| + | }, | ||
| + | { | ||
| + | "fcodeName":"capital of a political entity", | ||
| + | "toponymName":"Beijing", | ||
| + | "countrycode":"CN", | ||
| + | "fcl":"P", | ||
| + | "fclName":"city, village,...", | ||
| + | "name":"Peking", | ||
| + | "wikipedia":"en.wikipedia.org/wiki/Beijing", | ||
| + | "lng":116.397228240967, | ||
| + | "fcode":"PPLC", | ||
| + | "geonameId":1816670, | ||
| + | "lat":39.9074977414405, | ||
| + | "population":1171662 | ||
| + | }, | ||
| + | ... | ||
| + | ] | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | Astfel, informațiile care trebuie preluate din rezultat sunt: | ||
| + | ^ atribut JSON ^ tip de date ^ detaliu ^ | ||
| + | | ''lat'' | ''double'' | latitudine | | ||
| + | | ''lng'' | ''double'' | longitudine | | ||
| + | | ''toponymName'' | ''String'' | denumirea toponimului | | ||
| + | | ''population'' | ''long'' | populație | | ||
| + | | ''fcodeName'' | ''String'' | descriere localitate | | ||
| + | | ''name'' | ''String'' | nume obiectiv turistic | | ||
| + | | ''wikipedia'' | ''String'' | adresa paginii Internet pe care pot fi regăsite informații suplimentare, pe Wikipedia | | ||
| + | | ''countrycode'' | ''String'' | codul de țară | | ||
| + | |||
| + | Prin urmare, pașii care trebuie urmați pentru implementarea funcționalității sunt: | ||
| + | - construirea URL-ului, prin atașarea informațiilor preluate de la utilizator (coordonatele punctelor cardinale); | ||
| + | - execuția unei cereri HTTP; | ||
| + | - parsarea documentului JSON corespunzător răspunsului HTTP; <code java> | ||
| + | final ArrayList<LandmarkInformation> landMarkInformationList = new ArrayList<LandmarkInformation>(); | ||
| + | JSONObject result = new JSONObject(content); | ||
| + | JSONArray jsonArray = result.getJSONArray(Constants.GEONAMES); | ||
| + | for (int k = 0; k < jsonArray.length(); k++) { | ||
| + | JSONObject jsonObject = jsonArray.getJSONObject(k); | ||
| + | landMarkInformationList.add(new LandmarkInformation( | ||
| + | jsonObject.getDouble(Constants.LATITUDE), | ||
| + | jsonObject.getDouble(Constants.LONGITUDE), | ||
| + | jsonObject.getString(Constants.TOPONYM_NAME), | ||
| + | jsonObject.getLong(Constants.POPULATION), | ||
| + | jsonObject.getString(Constants.FCODE_NAME), | ||
| + | jsonObject.getString(Constants.NAME), | ||
| + | jsonObject.getString(Constants.WIKIPEDIA_WEB_PAGE_ADDRESS), | ||
| + | jsonObject.getString(Constants.COUNTRY_CODE))); | ||
| + | } | ||
| + | </code> | ||
| + | - atașarea informațiilor obținute în cadrul unui control grafic de tip listă. | ||
| + | |||
| + | **8.** Să se încarce modificările realizate în cadrul depozitului 'Laborator07' de pe contul Github personal, folosind un mesaj sugestiv. <code> | ||
| student@pdsd2015:~/Laborator07$ git add * | student@pdsd2015:~/Laborator07$ git add * | ||
| student@pdsd2015:~/Laborator07$ git commit -m "implemented taks for laboratory 07" | student@pdsd2015:~/Laborator07$ git commit -m "implemented taks for laboratory 07" | ||
| Line 535: | Line 925: | ||
| [[http://developer.android.com/reference/java/net/HttpURLConnection.html|HttpURLConnection]]\\ | [[http://developer.android.com/reference/java/net/HttpURLConnection.html|HttpURLConnection]]\\ | ||
| [[http://hc.apache.org/httpcomponents-client-4.4.x/index.html|Apache Http Components]]\\ | [[http://hc.apache.org/httpcomponents-client-4.4.x/index.html|Apache Http Components]]\\ | ||
| - | [[http://jsoup.org/|Jsoup]] | + | [[http://developer.android.com/reference/org/apache/http/client/HttpClient.html|HttpClient]], [[http://developer.android.com/reference/org/apache/http/client/methods/HttpGet.html|HttpGet]], [[http://developer.android.com/reference/org/apache/http/client/methods/HttpPost.html|HttpPost]]\\ |
| + | [[http://jsoup.org/|Jsoup]]\\ | ||
| + | [[http://en.wikipedia.org/wiki/JSON|Formatul JSON]]\\ | ||
| + | [[http://developer.android.com/reference/org/json/JSONObject.html|JSONObject]] | ||
