70-480 – Użytkowanie danych

70-480

Użytkowanie danych JSON i XML; pobieranie danych przy użyciu usług sieci Web; ładowanie danych lub pobieranie danych z innych źródeł przy użyciu obiektu XMLHTTPRequest.



Jak mówi opis ze strony Microsoft – dzisiaj zajmiemy się konsumpcją danych w formatach JSON oraz XML przy użyciu obiektu XMLHTTPRequest (odpowiednik dla IE5 i IE6, to obiekt ActiveXObject).

JSON | XML

Oba formaty pozwalają na wymianę danych w łatwy sposób. Są one również czytelne dla swoich użytkowników i zapewniają sposób opisu danych. To co jednak odróżnia JSON od XML, to “lekkość”. Ilość przesyłanych danych jest często znacznie mniejsza, a format odpowiada w zasadzie obiektom JavaScript, czy bazom danych typu NoSQL, takim jak na przykład MongoDB.

Przykład notacji JSON:

{
  "glossary":{
    "title":"example glossary",
    "GlossDiv":{
      "title":"S",
      "GlossList":{
        "GlossEntry":{
          "ID":"SGML",
          "SortAs":"SGML",
          "GlossTerm":"Standard Generalized Markup Language",
          "Acronym":"SGML",
          "Abbrev":"ISO 8879:1986",
          "GlossDef":{
            "para":"A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso":[
              "GML",
              "XML"
            ]
          },
          "GlossSee":"markup"
        }
      }
    }
  }
}

Przykład opisu tych samych danych, ale notacji XML:

<?xml version="1.0" encoding="UTF-8"?>
<glossary>
  <title>example glossary</title>
  <GlossDiv>
    <title>S</title>
    <GlossList>
      <GlossEntry ID="SGML" SortAs="SGML">
        <GlossTerm>Standard Generalized Markup Language</GlossTerm>
        <Acronym>SGML</Acronym>
        <Abbrev>ISO 8879:1986</Abbrev>
        <GlossDef>
          <para>A meta-markup language, used to create markup
languages such as DocBook.</para>
          <GlossSeeAlso OtherTerm="GML" />
          <GlossSeeAlso OtherTerm="XML" />
        </GlossDef>
        <GlossSee OtherTerm="markup" />
      </GlossEntry>
    </GlossList>
  </GlossDiv>
</glossary>

Warto również nadmienić, że JavaScript posiada natywne wsparcie dla notacji JSON i pozwala transformować obiekt JSON do formatu typu String i w drugą stronę. Służą do tego dwie funkcje:

  • stringify – transformuje obiekt JSON do String
  • parse – parsuje String i stara się z niego zbudować obiekt JSON

, które znajdziemy w obiekcie JSON.

Spójrzmy na przykład:

var json = {
  "glossary":{
    "title":"example glossary",
    "GlossDiv":{
      "title":"S",
      "GlossList":{
        "GlossEntry":{
          "ID":"SGML",
          "SortAs":"SGML",
          "GlossTerm":"Standard Generalized Markup Language",
          "Acronym":"SGML",
          "Abbrev":"ISO 8879:1986",
          "GlossDef":{
            "para":"A meta-markup language, used to create markup languages such as DocBook.",
            "GlossSeeAlso":[
              "GML",
              "XML"
            ]
          },
          "GlossSee":"markup"
        }
      }
    }
  }
};

var jsonAsString = JSON.stringify(json);
console.log(jsonAsString);

var againJson = JSON.parse(jsonAsString);
console.log(againJson);

Wersja online dostępna jest na jsfiddle.net Format JSON jest niezwykle często spotykany w komunikacji z wykorzystaniem serwisów typu REST.

Przykład operacji na notacji XML znajdziecie na stronie w3schools.com. Po raz kolejny odsyłam do dobrego źródła, bo nie chcę się powtarzać.

XMLHTTPRequest

O metodzie $.ajax oraz jej aliasach $.get i $.post pisałem już w poprzednich wpisach. Całość jednak opiera się na natywnym sposobie konsumowania tak zwanych web serwisów, z użyciem obiektu XMLHTTPRequest.

var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function() {
  if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    console.log(JSON.parse(xmlhttp.responseText));
  }
}
xmlhttp.open('GET', 'http://somefancyapi.com/returnjsonstring.json', true);
xmlhttp.send();

Całość wydaje się bajecznie prosta. Tworzymy obiekt, ustawiamy obsługę callback dla onreadystatechange, a następnie otwieramy połączenie i wysyłamy do serwera. Po odebraniu danych są one parsowane do JSON. Możemy jednak posłużyć się określeniem responseType, a odpowiedź zostanie nam zwrócona (jeśli nie wystąpi błąd) w żądanym formacie. Spójrzmy na kolejny przykład:

var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function() {
  if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    console.log(xmlhttp.response);
  }
}
xmlhttp.responseType = 'json';
xmlhttp.open('GET', 'http://somefancyapi.com/returnjsonstring.json', true);
xmlhttp.send();

Nie ma konieczności korzystania z funkcji parse obiektu JSON. Całość wydaje się być jeszcze prostsza.

O XMLHTTPRequest pisałem już w poprzednich postach, a będzie o nim także mowa w kolejnych, więc nie ma co przedłużać. Zapraszam do własnych prób i przejrzenia API, które oferuje nam XMLHTTPRequest.

Pobranie treści strony niczym Facebook – PHP / jQuery

pobranie-tresci-strony-niczym-facebook-php-jquery

Z pewnością wszyscy widzieli i znają sposób dodawania adresów URL do naszych statusów na Facebooku lub Google+. Mam oczywiście na myśli sytuację, gdy po chwili od wklejenia adresu URL w okno, gdzie tworzymy nasz status sprarsowana zostaje i pobrana treść ze strony, której adresu użyliśmy. Jest to zarazem efektowne i bardzo użyteczne, bo od razu widzimy zajawkę informacji, która kryje się pod danym URL. Pokażę Wam dzisiaj prosty sposób, który wykorzystuje język PHP i bibliotekę  jQuery wraz z zapytaniem Ajax. Już dawno przymierzałem się do podobnego wpisu i wreszcie finalnie mogę go Wam przedstawić.

Zacznijmy od obsługi pola, gdzie użytkownik podaje swój adres URL. Obsługiwać będzie je oczywiście kilka linijek kodu w jQuery, gdzie w pierwszej kolejności sparsować musimy wprowadzoną treść oraz sprawdzić istnienie w niej poprawnego adresu URL, a następnie wykonać zapytanie Ajax do skryptu w języku PHP.

Pobieramy treść

$(document).ready(function() {
    var getUrl = $('#get_url') // url to extract from text field
        match_url = /\b(https?):\/\/([\-A-Z0-9.]+)(\/[\-A-Z0-9+&@#\/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#\/%=~_|!:,.;]*)?/i // url to match in the text field
    ;

    getUrl.keyup(function() { // user types url in text field        
        // continue if matched url is found in text field
        if (match_url.test(getUrl.val())) {
            $("#results").hide();
            $("#loading_indicator").show(); // show loading indicator image

            var extracted_url = getUrl.val().match(match_url)[0]; // extracted first url from text filed

            // ajax request to be sent to extract-process.php
            $.post('extract-process.php', {'url': extracted_url}, function(data) {
                extracted_images = data.images;
                total_images = parseInt(data.images.length - 1);
                img_arr_pos = total_images;

                if (total_images > 0) {
                    inc_image = '<div class="extracted_thumb" id="extracted_thumb"><img src="' + data.images[img_arr_pos] + '" width="100" height="100"></div>';
                } else {
                    inc_image = '';
                }
                // content to be loaded in #results element
                var content = '<div class="extracted_url">' + inc_image + '<div class="extracted_content"><h4><a href="' + extracted_url + '" target="_blank">' + data.title + '</a></h4><p>' + data.content + '</p><div class="thumb_sel"><span class="prev_thumb" id="thumb_prev"> </span><span class="next_thumb" id="thumb_next"> </span> </div><span class="small_text" id="total_imgs">' + img_arr_pos + ' of ' + total_images + '</span><span class="small_text">  Choose a Thumbnail</span></div></div>';

                // load results in the element
                $("#results").html(content); // append received data into the element
                $("#results").slideDown(); // show results with slide down effect
                $("#loading_indicator").hide(); // hide loading indicator image
            }, 'json');
        }
    });
});

Wybieramy miniaturę

Skrypt w PHP “wypluje” nam takie dane jak tytuł, zajawkę oraz zdjęcie(-a) w formacie JSON. Jeżeli zdjęć jest kilka, to byłoby dobrze dać użytkownikowi wybór konkretnej miniatury.  Zróbmy to.

$(document).ready(function() {
    // user clicks previous thumbail
    $("body").on("click", "#thumb_prev", function(e) {
        if (img_arr_pos > 0)
        {
            img_arr_pos--; // thmubnail array position decrement

            // replace with new thumbnail
            $("#extracted_thumb").html('<img src="'+extracted_images[img_arr_pos]+'" width="100" height="100">');

            // replace thmubnail position text
            $("#total_imgs").html((img_arr_pos)+' of '+total_images);
        }
    });

    // user clicks next thumbail
    $("body").on("click", "#thumb_next", function(e) {
        if (img_arr_pos < total_images)
        {
            img_arr_pos++; // thmubnail array position increment

            // replace with new thumbnail
            $("#extracted_thumb").html('<img src="'+extracted_images[img_arr_pos]+'" width="100" height="100">');

            // replace thmubnail position text
            $("#total_imgs").html((img_arr_pos)+' of '+total_images);
        }
    });
});

HTML

Poniżej dosłownie 5 linijek HTML, który pozwoli użytkownikowi na wklejenie swojego aresu URL.

<div class="extract_url">
    <img id="loading_indicator" src="images/ajax-loader.gif" />
    <textarea id="get_url" placeholder="Enter Your URL here" class="get_url_input" spellcheck="false"></textarea>
    <div id="results"></div>
</div>

Czas na skrypt w PHP

Do pobrania treści z innej strony w naszym skrypcie PHP wykorzystamy PHP Simple HTML DOM Parser, który w niezwykle łatwy sposób pozwala sparsować drzewo DOM na podstawie przekazanego URL. Trzeba nadmienić, że wymaga on PHP w wersji 5+, ale chyba nie powinno być to dla nikogo problemem. Wszystkie zebrane dane są oczywiście zwracane w formacie JSON.

<?php
if (isset($_POST["url"])) {
    $get_url = $_POST["url"];

    // include PHP HTML DOM parser (requires PHP 5 +)
    include_once("include/simple_html_dom.inc.php");

    // get URL content
    $get_content = file_get_html($get_url);

    // get page title 
    foreach ($get_content->find('title') as $element) {
        $page_title = $element->plaintext;
    }

    // get body text
    foreach ($get_content->find('body') as $element) {
        $page_body = trim($element->plaintext);
        $pos = strpos($page_body, ' ', 200); // find the numeric position to substract
        $page_body = substr($page_body, 0, $pos); // shorten text to 200 chars
    }

    $image_urls = array();

    // get all images URLs in the content
    foreach ($get_content->find('img') as $element) {
        // validate image name
        if (!preg_match('/blank.(.*)/i', $element->src) && filter_var($element->src, FILTER_VALIDATE_URL)) {
            $image_urls[] = $element->src;
        }
    }

    // prepare for JSON 
    $output = array('title' => $page_title, 'images' => $image_urls, 'content' => $page_body);
    echo json_encode($output); // output JSON data
}
?>

Tym oto sposobem mamy gotowe rozwiązanie! Poniżej link do pobrania całości.

Pobranie treści strony niczym Facebook – PHP / jQuery

Parsowanie XML z SimpleXML w PHP

XML SimpleXML PHP

Parsowanie składni XML oznacza zwykle nawigowanie po całej strukturze dokumentu XML. Duża liczba dzisiejszych serwisów zwraca dane w formacie JSON, ale jest jeszcze sporo takich, które używają do tego celu XML.

W dzisiejszym HowTo wykorzystam rozszerzenie SimpleXML, które wprowadzone zostało do PHP 5.0. Pozwala ono na łatwe nawigowanie po dokumencie XML.

Przejdźmy do pierwszego przykładu wykorzystania. Mamy do dyspozycji plik languages.xml:

<?xml version="1.0" encoding="utf-8"?>
<languages>
 <lang name="C">
  <appeared>1972</appeared>
  <creator>Dennis Ritchie</creator>
 </lang>
 <lang name="PHP">
  <appeared>1995</appeared>
  <creator>Rasmus Lerdorf</creator>
 </lang>
 <lang name="Java">
  <appeared>1995</appeared>
  <creator>James Gosling</creator>
 </lang>
</languages>

Powyższy dokument opisuje w pewien sposób trzy języki programowania i podaje ich kilka charakterystyk.

Do odczytania tego dokumentu wykorzystam funkcję simple_load_file().

<?php
$languages = simplexml_load_file("languages.xml");

Istnieje jeszcze funkcja simplexml_load_string(), która jak nie trudno się domyślić, ładuje do pamięci łańcuch znaków, który jest poprawną składnią dokumentu XML. Obie funkcje wczytują dokument DOM do pamięci tworząc tym samym obiekt SimpleXMLElement.

Wykonując var_dump($languages) lub print_r($languages) otrzymamy:

SimpleXMLElement Object
(
    [lang] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [name] => C
                        )
                    [appeared] => 1972
                    [creator] => Dennis Ritchie
                )
            [1] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [name] => PHP
                        )
                    [appeared] => 1995
                    [creator] => Rasmus Lerdorf
                )
            [2] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [name] => Java
                        )
                    [appeared] => 1995
                    [creator] => James Gosling
                )
        )
)

Widać tutaj główny, publiczny element lang (SimpleXMLElement), który jest tablicą i zawiera trzy elementy.

By dostać się do konkretnych elementów trzeba wykorzystać operator ->

<?php
$languages->lang[0]->appeared;
$languages->lang[0]->creator;

Można także iterować po liście za pomocą metody foreach

<?php
foreach ($languages->ang as $lang) {
    echo "Language: ".$lang["name"];
    echo "Appeared: ".$lang->appeared;
    echo "Creator: ".$lang->creator;
}

Należy zwrócić uwagę, że do wartości atrybutu lang docieramy za pomocą parametru w tablicy, a do publicznych elementów appeared i creator za pomocą operatora ->.

Wszystko fajnie, pięknie, ale co jeżeli trzeba sobie poradzić z przestrzenią nazw, która zdefiniowana została dla poszczególnych elementów? Jest i na to sposób!

Zmodyfikuję teraz plik languages.xml do postaci:

<?xml version="1.0" encoding="utf-8"?>
<languages
 xmlns:dc="http://purl.org/dc/elements/1.1/">
 <lang name="C">
  <appeared>1972</appeared>
  <dc:creator>Dennis Ritchie</dc:creator>
 </lang>
 <lang name="PHP">
  <appeared>1995</appeared>
  <dc:creator>Rasmus Lerdorf</dc:creator>
 </lang>
 <lang name="Java">
  <appeared>1995</appeared>
  <dc:creator>James Gosling</dc:creator>
 </lang>
</languages>

Element creator posiada teraz namespace, który kieruje na http://purl.org/dc/elements/1.1/. Dostęp do tego elementu poprzez wykorzystanie operatora -> nie jest już możliwy.

By poradzić sobie z tym problemem trzeba zrobić coś takiego:

<?php
$dc = $languages->lang[1]- >children("http://purl.org/dc/elements/1.1/");
echo $dc->creator;

Metoda children() pobiera namespace i zwraca wartość elementu dla którego dc jest prefixem.

Istnieje inna możliwość odczytania wartości elementu creator:

<?php
$namespaces = $languages->getNamespaces(true);
$dc = $languages->lang[1]->children($namespaces["dc"]);

echo $dc->creator;

Pobieramy w tym celu wszystkie namespace użyte w dokumencie XML i za pomocą elementu w tablicy odwołujemy się do konkretnego namespace i pobieramy wartość dla którego jest on prefixem.

I podobnie jak wcześniej – przykład iterowania po wszystkich elementach:

<?php
$languages = simplexml_load_file("languages.xml");
$ns = $languages->getNamespaces(true);

foreach ($languages->lang as $lang) {
    $dc = $lang->children($ns["dc"]);
    echo "Language: ".$lang["name"];
    echo "Appeared: ".$lang->appeared;
    echo "Creator: ".$dc->creator;
}

Czas na praktyczny przykład. Parsowanie kanału YouTube.

W tym celu należy wykorzystać API dostępne pod adresem: http://gdata.youtube.com/feeds/api/users/{channel}/uploads, gdzie w miejscu {channel} podana zostanie nazwa konkretnego użytkownika.

W danych, które zostaną zwrócone w przypadku podania poprawnej nazwy użytkownika, dla każdego filmu zdefiniowane będą:

  • jego URL
  • miniatura
  • tytuł
<?php
$channel = "channelName";
$url = "http://gdata.youtube.com/feeds/api/users/".$channel."/uploads";
$xml = file_get_contents($url);

$feed = simplexml_load_string($xml);
$ns = $feed->getNameSpaces(true);

Spoglądając w strukturę otrzymanego XML możemy zauważyć, że ważne dla nas dane znajdują się w elemencie group, który poprzedzony jest namespace media.

<entry>
   …
   <media:group>
      …
      <media:player url="video url"/>
      <media:thumbnail url="image url" height="height" width="width"/>
      <media:title type="plain">Title…</media:title>
      …
   </media:group>
   …
</entry>

Wystarczy, że ponownie wykorzystana zostanie metoda foreach, dzięki której będziemy w stanie wyświetlić odpowiednie dla nas dane.

<?php
foreach ($feed->entry as $entry) {
	$group = $entry->children($ns["media"]);
	$group = $group->group;
	$thumbnailAttrs = $group->thumbnail[1]->attributes();
	$image = $thumbnailAttrs["url"];
	$player = $group->player->attributes();
	$link = $player["url"];
	$title = $group->title;
	
	echo "<p><a href=".$link."><img src=".$image." alt=".$title."></a></p>";
}

Podsumowując. Operowanie na dokumentach XML jest bardzo proste. Dzięki wykorzystaniu rozszerzenia SimpleXML, drzewo DOM nie jest już dla nas problemem.

Pełną dokumentację SimpleXML znajdziecie pod adresem: http://php.net/manual/en/book.simplexml.php

Roundup #10 – OpenKeyval, Socialist, Reveal, SwipeView, BookBlock, Happy, jsDraw2DX

Roundup #10 - OpenKeyval, Socialist, Reveal, SwipeView, BookBlock, Happy, jsDraw2DX

OpenKeyval

OpenKeyval is a completely open key-value data store, exposed as a drop-dead simple web service. The goal is to make this a very easy way to persist data in web applications.

OpenKeyval – Instant & Web-Based Database For Any Project


Socialist

Socialist is a jQuery social plugin that creates a social stream (or social “wall”) from multiple social media feeds in one place. Use it to pull content from Facebook pages, Twitter, LinkedIn, YouTube and other social. networks..

Socialist – A jQuery social media plugin


Reveal

reveal.js is a framework for easily creating beautiful presentations using HTML. You’ll need a browser with support for CSS 3D transforms to see it in its full glory.

The Beautiful HTML Presentation Framework


SwipeView

SwipeView is the super simple solution to endless seamlessly loopable carousels for the mobile browser. It’s memory conservative as it uses only three elements at any given time, it’s smooth as velvet since it takes advantage of hardware acceleration, it’s bandwidth friendly with its 1.5kb minified/gzipped footprint.

SwipeView – Infinite Carousels For Mobile Web


BookBlock

A jQuery plugin that will create a booklet-like component that let’s you navigate through its items by flipping the pages.

v


Happy

Yes, there a million form validation plugins already – but I like this approach and the good news is, if you don’t, you have other options. In fact, if you want something really full-featured for jQuery. Use this one. Personally I wanted something really lightweight and extendable (because it’s hard to be happy when you’re bloated).

Lightweight jQuery Form Validation Plugin: Happy.js


jsDraw2DX

jsDraw2DX is a standalone JavaScript library for creating any type of interactive graphics with SVG (and VML for old IE browsers).

Besides the ability to generate all basic shapes like line, rectangle, polygon, circle, ellipse, arc, etc., the library can draw curves, beziers (any degree), function plots, images and decorated text.

SVG Graphics Library For JavaScript: jsDraw2DX


[1], [2], [3], [4]