понедельник, 8 августа 2011 г.

Использование JSON в GWT

     JSON (JavaScript Object Notation) - это универсальный, легкий и удобный текстовый формат обмена данными, основанный на языке JavaScript. Как и XML, он очень легко воспринимается людьми. JSON является независимым от языка обработки, существуют специальные библиотеки практически во всех языках программирования. В то время, как XML часто используется, например, для хранения настроек программ, то JSON больше используется для работы в сети. 
     Рассмотрим использование JSON в GWT приложении. В GWT наиболее общий путь взаимодействия с сервером происходит через GWT-RPC, так как он делает все связи на Java особенно простыми и эффективными. JSON используется когда общение с сервером происходит через простые HTTP вызовы. 
     Создадим веб проект в Eclipse (File - New - Web Application Project) и назовем его “JsonGwtApp”. Для работы с JSON на клиентской стороне, в файл JsonGwtApp.gwt.xml следует добавить следующую строку:
<inherits name="com.google.gwt.json.JSON" />
Создадим модель объекта, который будет использоваться для хранения данных.
package com.dmitrynikol.json.gwt.server.model;

import java.util.LinkedList;
import java.util.List;

public class Book {

  public String id;
  public String title;
  public String author;
  public List<Double> prices = new LinkedList<Double>();

  public Book(String id, String title, String author, List<Double> prices) {
    this.id = id;
    this.title = title;
    this.author = author;
    this.prices = prices;
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
  public String getAuthor() {
    return author;
  }
  public void setAuthor(String author) {
    this.author = author;
  }
  public List<Double> getPrices() {
    return prices;
  }
  public void setPrices(List<Double> prices) {
    this.prices = prices;
  }
}
     Теперь пришла очередь к серверной части приложения. Создадим сервлет, который будет использоваться для имитации сервера, от которого мы будет получать данные. Сервлет создает JSON ответ клиенту используя список книг. Также следует добавить в classpath проекта библиотеку json.jar и скопировать ее в “war\WEB-INF\lib”.
package com.dmitrynikol.json.gwt.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.dmitrynikol.json.gwt.server.model.Book;
import org.json.JSONObject;

public class BooksServlet extends HttpServlet {

  private static final long serialVersionUID = 5984818046261512114L;

  private static List<Book> books = new LinkedList<Book>();

  static {
    books.add(new Book("132529", "The Hound of the Baskervilles",
        "Arthur Conan Doyle", Arrays.asList(40.0, 45.2)));
    books.add(new Book("745353", "The Man in the Brown Suit",
        "Agatha Christie", Arrays.asList(37.0, 40.3)));
    books.add(new Book("248941", "Liberty Bar", "Georges Simenon",
        Arrays.asList(34.2, 37.7)));
    books.add(new Book("478134", "The Case of the Turning Tide",
        "Erle Stanley Gardner", Arrays.asList(25.5, 28.3)));
    books.add(new Book("328473", "The Dogs of War", "Frederick Forsyth",
        Arrays.asList(25.0, 30.3)));
  }

  @Override
  protected void doGet(HttpServletRequest request,
      HttpServletResponse response) throws ServletException, IOException {
    try {
      JSONObject responseObject = new JSONObject();
      List<JSONObject> booksObjects = new LinkedList<JSONObject>();
   
      for (Book book : books) {
        JSONObject booksObj = new JSONObject();

        booksObj.put("id", book.getId());
        booksObj.put("title", book.getTitle());
        booksObj.put("author", book.getAuthor());
     
        List<JSONObject> pricesObjects = new LinkedList<JSONObject>();
        for (Double price : book.getPrices()) {
          JSONObject priceObj = new JSONObject();
          priceObj.put("price", price);
          pricesObjects.add(priceObj);
        }
     
        booksObj.put("prices", pricesObjects);
     
        booksObjects.add(booksObj);
      }
   
      responseObject.put("books", booksObjects);
   
      PrintWriter writer = response.getWriter();
      writer.write(responseObject.toString());
      writer.flush();
    } catch (Exception ex) {
      ex.printStackTrace();
      throw new ServletException();
    }
  }
}

   Сервлет нам будет возвращать след. данные:
{
 "books": [
  {
   "id": "132529",
   "author": "Arthur Conan Doyle",
   "title": "The Hound of the Baskervilles",
   "prices": [
    {
     "price": 40
    },
    {
     "price": 45.2
    }
   ]
  } … и т.д.
 ]
}
     Сконфигурируем сервлет так, чтобы он отвечал на определенный url. Нужно поправить файл web.xml (war\WEB-INF\web.xml). Он должен выглядеть так:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <servlet>
    <servlet-name>BooksServlet</servlet-name>
    <servlet-class>com.dmitrynikol.json.gwt.server.BooksServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>BooksServlet </servlet-name>
    <url-pattern>/jsongwtapp/books</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>JsonGwtApp.html</welcome-file>
  </welcome-file-list>
</web-app>
     Осталось еще написать клиентскую часть. Создадим стартовую точку GWT приложения. На клиентской стороне последовательность действий выглядит так: выполняется HTTP GET вызов у сервера, приходит ответ в JSON формате и выполняется парсинг ответа.
package com.dmitrynikol.json.gwt.client;

import java.util.LinkedList;
import java.util.List;

import com.dmitrynikol.json.gwt.client.widget.BooksWidget;
import com.dmitrynikol.json.gwt.client.widget.IBook;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;

public class JsonGwtApp implements EntryPoint {

  @Override
  public void onModuleLoad() {
    final Button tableBooksButton = new Button("Show books");

    RootPanel.get("showTableBooksButton").add(tableBooksButton);

    tableBooksButton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        getDataFromServer();
      }
    });
  }

  public void getDataFromServer() {
    try {
      RequestBuilder requestBuilder = new RequestBuilder(
          RequestBuilder.GET, "/jsongwtapp/books");

      requestBuilder.setCallback(new RequestCallback() {
        @Override
        public void onResponseReceived(Request request,
            Response response) {
          BooksWidget<IBook> booksWidget = new BooksWidget<IBook>();
          List<IBook> books = parseJsonData(response.getText());
          if (books != null) {
            booksWidget.setData(books);
            booksWidget.show();
          }
        }

        @Override
        public void onError(Request request, Throwable exception) {
          Window.alert(exception.getMessage());
        }
      });
      requestBuilder.send();
    } catch (RequestException ex) {
      Window.alert(ex.getMessage());
    }
  }

  public List<IBook> parseJsonData(String json) {
    JSONValue value = JSONParser.parseStrict(json);
    JSONObject booksObject = value.isObject();
    JSONArray booksArray = booksObject.get("books").isArray();

    if (booksArray != null) {
      List<IBook> books = new LinkedList<IBook>();
      for (int i = 0; i <= booksArray.size() - 1; i++) {
        JSONObject bookObj = booksArray.get(i).isObject();

        String id = bookObj.get("id").isString().stringValue();
        String title = bookObj.get("title").isString().stringValue();
        String author = bookObj.get("author").isString().stringValue();

        JSONArray pricesArray = bookObj.get("prices").isArray();
        List<Double> prices = new LinkedList<Double>();
        if (pricesArray != null) {
          for (int k = 0; k <= pricesArray.size() - 1; k++) {
            JSONObject priceObj = pricesArray.get(k).isObject();
            double price = priceObj.get("price").isNumber().doubleValue();
            prices.add(price);
          }
        }

        IBook book = new IBook(id, title, author, prices);
        books.add(book);
      }
      return books;
    }
    return null;
  }
}
     Для HTTP вызова используется класс RequestBuilder и ассинхронный метод обратного вызова класса RequestCallback. Далее просто вызываем метод send() для отправки HTTP запроса основанного на текущей конфигурации. Информацию о книгах выведем в таблицу, а саму таблицу поместим в диалоговое окно.
package com.dmitrynikol.json.gwt.client.widget;

import java.util.LinkedList;
import java.util.List;

public class IBook {

  public String id;
  public String title;
  public String author;
  public List<Double> prices = new LinkedList<Double>();

  public IBook(String id, String title, String author, List<Double> prices) {
    this.id = id;
    this.title = title;
    this.author = author;
    this.prices = prices;
  }
  public String getId() {
    return id;
  }
  public String getTitle() {
    return title;
  }
  public String getAuthor() {
    return author;
  }
  public List<Double> getPrices() {
    return prices;
  }
  public void setPrices(List<Double> prices) {
    this.prices = prices;
  }
}
package com.dmitrynikol.json.gwt.client.widget;

import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Widget;

public class BooksWidget<T extends IBook> extends DialogBox {
  public static BooksWidgetUiBinder uiBinder = GWT.create(BooksWidgetUiBinder.class);

  public interface BooksWidgetUiBinder extends UiBinder<Widget, BooksWidget<?>> {
  }

  @UiField CellTable<T> bookTable;
  @UiField Anchor close;

  public BooksWidget() {
    setWidget(uiBinder.createAndBindUi(this));
    setText("Detective books");
  }

  public void setData(List<T> books) {
    bookTable.addColumn(new TextColumn<T>() {
      @Override
      public String getValue(T object) {
        return object.getId();
      }
    }, "Id");
    bookTable.addColumn(new TextColumn<T>() {
      @Override
      public String getValue(T object) {
        return object.getTitle();
      }
    }, "Title");
    bookTable.addColumn(new TextColumn<T>() {
      @Override
      public String getValue(T object) {
        return object.getAuthor();
      }
    }, "Author");
    bookTable.addColumn(new TextColumn<T>() {
      @Override
      public String getValue(T object) {
        List<Double> prices = object.getPrices();
        StringBuffer priceBuf = new StringBuffer();
        for (Double price : prices) {
          priceBuf.append(price + " ");
        }
        return priceBuf.toString();
      }
    }, "Price");
 
    bookTable.setRowCount(books.size(), true);
    bookTable.setRowData(0, books);
  }

  @UiHandler("close")
  void onCloseClicked(ClickEvent event) {
    hide();
  }
}
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"><ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
  xmlns:g="urn:import:com.google.gwt.user.client.ui"
  xmlns:c='urn:import:com.google.gwt.user.cellview.client'
>

  
<g:HTMLPanel>
    <div
>
      
<c:CellTable ui:field="bookTable"/>
    </div
>
    
<div>
      
<g:Anchor ui:field="close" text="close"/>
    </div
>
  </g:HTMLPanel
>
</ui:UiBinder
>

Также следует изменить файл JsonGwtApp.html, т.к. мы обращаемся к елементу по id.
.....
 
<h1 align="center">JSON GWT Application</h1
>
 
<div align="center" id="showTableBooksButton" /
>
.....
     После запуска приложения, на страничке браузера можно увидеть заголовок и кнопку для вывода информации о книгах. После клика на кнопке получаем JSON данные, выполняется парсинг и данные выводятся на экран. 


     Вот и все. Eclipse проект можно скачать по следующей ссылке.

Комментариев нет:

Отправить комментарий