Threaded View

  1. #1
    Sencha User
    Join Date
    May 2012
    Posts
    19
    Answers
    4
    Vote Rating
    0
    BionicGeek is on a distinguished road

      0  

    Default Answered: Can the GXT JsonReader (using AutoBeans) parse complex JSON structures?

    Answered: Can the GXT JsonReader (using AutoBeans) parse complex JSON structures?


    I am currently encountering an issue whereby I need to work with data returned from a service which has a more complex JSON structure than the GXT examples and thus far I cannot find any documentation or example which demonstrates how this might be accomplished.

    The JSON contains multiple key/value pairs, but some of the key/value pairs are collections. I can have all of the data returned to me in one call from the service in the proper structure, but there does not appear to be a way to parse the data into separate entities. In my particular case I am attempting to configure a loader which will process one of the collections but I also need other key/value pairs from the same message (it is not ok to have the loader make one call and then have another call made for the same data and retrieve the other key/value pairs). Is there any way to accomplish this using GXT3?

    Example: let's assume I can make a request from a server which returns JSON containing the name of an author along with a collection of the books the author has written. I want to display the author's name above a grid which lists the books. I want only one request made to the server and then have my view display the author in one component and the book list in a grid. Assume I need a loader instead of just a store as the grid may have to make additional calls (e.g. if it is a paging grid, livegrid, etc.).

    Example JSON: (one JSON message returned with and author element along with a collection of book elements)

    Code:
    { "returnData" : {"author" : "AuthorName"}, {"books" : {"id" : "1", "name" : "Book1"},{"id" : "2", "name" : "Book2"}}}
    Using the example for JsonReader (see the javadoc for an example) I can receive the request and parse the links into a collection using AutoBeans. This will work find when I need to have those retrieved and parsed in a loader. However, if I do that then the other properties are ignored. I currently don't see any way to parse the other values in the same request so they can be used elsewhere. My example code for the collection processing is below:

    CODE SAMPLES


    Code:
    // this is the root JSON object, the AuthorRecord
    public interface AuthorRecord {
       @PropertyName(value="author")
       String getAuthor();
       @PropertyName(value="author")
       void setAuthor(String author);
       @PropertyName(value="books")
       List<Book> getBooks();@
       @PropertyName(value="books")
       void setBooks (List<Book> books);
    }
    
    // models the book objects returned
    public interface Book {
       @PropertyName(value="id")
       String getId();
       @PropertyName(value="id")
       void setId(String id);
       @PropertyName(value="name")
       String getName();
       @PropertyName(value="name")
       void setName(String name);
    }
    
    public interface ReturnData {
       AuthorRootObject getAuthorRoot();
    }
    
    public interface LibraryAutoBeanFactory extends AutoBeanFactory {
       AutoBean<ReturnData> authorRecord();
       AutoBean<ListLoadConfig> loadConfig();
    }
    
    public class ReturnDataJsonReader extends JsonReader<ListLoadResult<Book>, ReturnData> {
      public ReturnDataJsonReader(AutoBeanFactory factory, Class<ReturnData> rootBeanType) {
         super(factory, rootBeanType);
      }
    
      @Override
      protected ListLoadResultBean<Book> createReturnData(Object loadConfig, ReturnData incomingData) {
         return new ListLoadResultBean<Book>(incomingData.getBooks());
       }
    }
    So the ReturnDataJsonReader works as expected when passed into a loader, but obviously the way this is currently written, the Author value is lost. Given that I only want to make one call to the server to retrieve the message, I do not currently see a way to configure a loader to process the list returned while also retrieving the other property (in this case Author) for use in another component on the same view. Is there a way to do this without abandoning the JsonReader?

  2. Thank you for the quick reply. As it happens I think you provided an answer to the question I was asking but after re-readnig my post and your respose I realized that I didn't complete the example enough to illustrate what I was trying to accomplish. Nevertheless your response gave me the idea I needed to solve the issue.

    The problem I was having was that I need to have a view that includes a grid (paging grid, etc.) which lists out the books, while having the Author's name sit above the grid. I wanted to get all of this information (or at least the first page of results) with only one request to the server since the JSON message contains all the information I need to accomplish this. The problem is that the loader makes the request and receives the response, and it expects that the reader it will use is going to process a collection. In my case, I need the loader to process the collection of books but also populate another data field. The solution I found was to create an arbitrary collection to pass to the loader and then implement my own load handler to process the return object as needed.

    1. The JSON being returned is really just one object of type ReturnData. The extended JsonReader could process this using AutoBeans, but if the reader is to be used for the loader, it needs to return a collection. Therefore, override the createReturnData() method to return a collection of one object.

    Code:
    public class ReturnDataJsonReader extends JsonReader<ListLoadResult<AuthorRecord>, ReturnData> {   
       public ReturnDataJsonReader(AutoBeanFactory factory, Class<ReturnData> rootBeanType) {      
          super(factory, rootBeanType);   
       }    
       @Override   
        protected ListLoadResultBean<AuthorRecord> createReturnData(Object loadConfig, ReturnData incomingData) {       
              List<AuthorRecord> authorDataCollection = new ArrayList<AuthorRecord>();
              authorDataCollection.add(incomingData);
              return authorDataCollection;    
        } 
    }
    2. The LoadHandler used in the examples takes a ListStore as an input and populates it with the results from the loader. Since the return object is not what we want populating the loader, and since we need to populate another property on the view, create your own LoadHandler to take the objects needed as input and populate them:

    View Class Example:

    Code:
    public class ExampleViewClass {
       // truncating most of the code in here for brevity
       // note some of the objects referenced here use the first post in the thread
       private String authorName;
       private ListStore<Book> bookList;
    
        // IMPORTANT - create your own LoadHandler
       private class LibraryLoadResultistStoreBinding<C, M, D extends ListLoadResult<AuthorRecord>> implements LoadHandler<ListLoadConfig, ListLoadResult<AuthorRecord>> {
             private final ListStore<Book> bookStore;
             private final String authorName;
    
            public LibraryLoadResultistStoreBinding(ListStore<Book> books, String author) {
                  this.bookStore = books;
                  this.authorName = author;
            }
    
            @Override
            public void onLoad(LoadEvent<ListLoadConfig, ListLoadResult<AuthorRecord> event) {
               AuthorRecord response = event.getLoadResult().getData().get(0);  // the response object
               bookStore.replaceAll(response.getBooks());
               author = response.getAuthor();
            }
       }
    
    // example uses an HttpProxy but that's not required
    public void populateView() {
        LibraryAutoBeanFactory factory = GWT.create(LibraryAutoBeanFactory.class);
        ReturnDataJsonReader reader = new ReturnDataJsonReader(factory, ReturnData.class);
        String path = "http://path.to.resource/getinfo";
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
        HttpProxy<ListLoadConfig> proxy = new HttpProxy<ListLoadConfig>(builder);
    
        final ListLoader<ListLoadConfig, ListLoadResult<AuthorRecord>> loader = new       
             ListLoader<ListLoadConfig, ListLoadResult<AuthorRecord>> (proxy, reader);
    
        loader.useLoadConfig(ReturnDataAutoBeanFactory.instance.loadConfig().as();
    
        loader.addLoadHandler(new LibraryLoadResultistStoreBinding<ListLoadConfig, AuthorRecord,    
             ListLoadResult<AuthorRecord>>(bookList, authorName);  // pass in the objects to be populated
    
        loader.load();  // fire the loader

Thread Participants: 1

Tags for this Thread