JSON Processing in JavaEE 7- Using Streaming API

If you are wondering what JSON is (Read : What is JSON?), you might want to read this article of mine. Java didn’t have a support for JSON processing out of the box. Developers had to include external libraries to enable JSON processing. But with JSR 353 we have support for JSON processing in Java and this has been included in the latest JavaEE version i.e JavaEE 7. There are 2 variants on offer for JSON Processing those are:

  1. Streaming API
  2. Object Model API

Both the variants offer JSON Parsing as well as JSON construction. In this post we will look at the JSON Streaming API support in JavaEE 7. In Head First HTML5 there is a chapter on Web Storage where in the authors develop a sample application to explain the concept. I am making use of the same sample application and adding to it the support for sending the client side data to the server for further processing.

The sample application allows creation and deletion of sticky notes which are saved in the browsers local storage. This is the same example provided in Head First HTML5 book. I am enhancing the same sample application to allow syncing the sticky notes to the server. Syncing to the server in this sample application includes the following:

  1. Creating a JSON string for all the sticky notes stored locally in the browser.
  2. Sending the JSON string created in (1) above to the server via the WebSocket communication channel.
  3. The server end point parsing the JSON string to extract the sticky notes and store it in a Map.

With a brief introduction about the sample being discussed in the post, let me outline the various areas or concepts I am going to cover in this post, namely:

  1. Storing the sticky notes in the browser local storage.
  2. Creating JSON representation of all the sticky notes.
  3. Parsing the JSON data in the server.

Storing the sticky notes in the browser local storage

As I mentioned at the beginning of this post that the sample which allows creating, storing and deleting of sticky notes in browser local storage is taken from the Head First HTML5 book and its source code for all the chapters in the book can be downloaded from here. But if you just want to try out the sample discussed in this post, then you need not download anything as I am going to post the relevant code here. The main focus of this post is to show the JSON processing capability in JavaEE 7 and not the HTML5 local storage feature. But I am sure this will be a very handy sample to understand and try out the HTML5 local storage feature as well.

Lets look at the JSP which declares the HTML components required for Adding Sticky notes, displaying sticky notes and deleting the sticky notes:

<%--
    Document   : notetoself.jsp
--%>

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <title>Note to Self</title>
    <meta charset=”utf-8”>
    <link rel="stylesheet" href="notetoself.css">
    <script src="notetoself.js"></script>
  </head>
  <body>
    <form>
      <input type="text" id="note_text">
      <input type="button" id="add_button" value="Add Sticky Note to Self">
      <input type="button" id="sync_button" value="Sync sticky notes to server">
      <div id="sync_status"></div>
    </form>
    <!-- Listing all the sticky notes below -->
    <ul id="stickies">
    </ul>
  </body>
</html>

The above code is very straight forward: It creates 2 buttons and an input text box. The buttons add sticky notes to the local storage and sync the sticky notes to the server respectively. Lets look at the Javascript code which binds the buttons with the event handlers and stores the stick notes into the local storage:

//File name: notetoself.js
window.onload = init;

var websocket;
function init() {

  var button = document.getElementById("add_button");
  button.onclick = createSticky;

  //Loading all the sticky notes in the local storage and adding it to HTML DOM.
  var stickiesArray = getStickiesArray();
  for (var i = 0; i < stickiesArray.length; i++) {
    var key = stickiesArray[i];
    var value = localStorage.getItem(key);
    addStickyToDom(key, value);
  }
}

/**
 * Event handler for the button which adds the notes.
 */
function createSticky() {
  var value = document.getElementById("note_text").value;
  var currentDate = new Date();
  var time = currentDate.getTime();

  //Creating the key for the sticky note
  var key = "sticky_" + time;
  //Storing the sticky note with the key in the local storage.
  localStorage.setItem(key, value);

  var stickiesArray = getStickiesArray();
  //Only the key is stored in the stickies array.
  stickiesArray.push(key);
  //And the array of sticky note keys is stored in local storage.
  localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));

  addStickyToDom(key, value);

}

/**
 * Function to add the sticky data to the HTML DOM.
 */
function addStickyToDom(key, value) {
  var stickies = document.getElementById("stickies");
  var sticky = document.createElement("li");
  sticky.setAttribute("id", key);
  var span = document.createElement("span");
  span.setAttribute("class", "sticky");
  span.innerHTML = value;
  sticky.appendChild(span);
  stickies.appendChild(sticky);
  sticky.onclick = deleteSticky;
}

/**
 * Function to load the sticky note data from the local storage.
 */
function getStickiesArray() {
  var stickiesArray = localStorage.getItem("stickiesArray");
  if (!stickiesArray) {
    stickiesArray = [];
    localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
  }
  else {
    stickiesArray = JSON.parse(stickiesArray);
  }

  return stickiesArray;
}
/**
 * Function to delete the sticky note from the local storage.
 */
function deleteSticky(e) {
  var key = e.target.id;
  if (e.target.tagName.toLowerCase() == "span") {
    key = e.target.parentNode.id;
  }
  localStorage.removeItem(key);
  var stickiesArray = getStickiesArray();
  if (stickiesArray) {
    for (var i = 0; i < stickiesArray.length; i++) {
      if (key == stickiesArray[i]) {
        stickiesArray.splice(i, 1);
      }
    }
    localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
    removeStickyFromDom(key);
  }
}

/**
 * Function to remove the sticky note element present in the HTML DOM.
 */
function removeStickyFromDom(key) {
  var sticky = document.getElementById(key);
  sticky.parentNode.removeChild(sticky);
}

And we need to add the CSS file as well so that the sticky notes really appear like the sticky notes!! The CSS code is given below:

/*
   notetoself.css

Tested in: Firefox4RC, Safari 5, Opera 11, Chrome 10
Transition (ease-in) doesn't work in Firefox 3.

*/

body {
  background-color: #dbdbdb;
  font-size: 100%;
}

form input#note_text {
  width: 350px;
}

/* sticky note */

ul#stickies li {
  display: block;
  list-style: none;
  z-index: 1;
  float: left;
  margin: 30px;
  padding: 15px 15px 50px 15px;
  width: 200px;
  height: 200px;
  border: 1px solid #bfbfbf;
  background-color: LightGoldenRodYellow;  /* use #fafad2 if name doesn't work */
  color: black;
  text-decoration: none;
  -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
  -moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
  -o-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
  box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);
  -webkit-transition: all 0.5s ease-in;
  -moz-transition: all 0.5s ease-in;
  -o-transition: all 0.5s ease-in;
  -ms-transition: all 0.5s ease-in;
  transition: all 0.5s ease-in;
  overflow: hidden;
}

ul#stickies li span.sticky {
  font-family: Verdana, Helvetica, sans-serif;
  font-size: 200%;
}

/*
 * rotation
 */
ul#stickies li:nth-child(even) {
  -webkit-transform: rotate(2deg);
  -moz-transform: rotate(2deg);
  -o-transform: rotate(2deg);
  -ms-transform: rotate(2deg);
  transform: rotate(2deg);
}

ul#stickies li:nth-child(odd) {
  -webkit-transform: rotate(-1deg);
  -moz-transform: rotate(-1deg);
  -o-transform: rotate(-1deg);
  -ms-transform: rotate(-1deg);
  transform: rotate(-1deg);
}

ul#stickies li:nth-child(3n) {
  -webkit-transform: rotate(1deg);
  -moz-transform: rotate(1deg);
  -o-transform: rotate(1deg);
  -ms-transform: rotate(1deg);
  transform: rotate(1deg);
}

/*
   Transform demo
   Uses the transition (defined above) to ease in.
*/
ul#stickies li:hover {
  -webkit-box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.4);
  -moz-box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.4);
  -o-box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.4);
  -webkit-transform: rotate(0deg) scale(1.25);
  -moz-transform: rotate(0deg) scale(1.25);
  -o-transform: rotate(0deg) scale(1.25);
  -ms-transform: rotate(0deg) scale(1.25);
  transform: rotate(0deg) scale(1.25);
  z-index: 10;
}

You need not worry too much about the CSS code as the main focus of this article is not the CSS code. The Head First series call such code as “Ready Bake code”, so lets take this code as is and use it in our sample. Also note that we haven’t added any event handler to the “Sync Sticky notes to server” button, as this will be taken up in the next section.

Once you deploy the JSP, the Javascript and the CSS and load the JSP in the browser (in my case the URL will be: http://localhost:8080/DemoApplication/notetoself.jsp) and add few sticky notes you would be able to see something similar to below:
1

Creating JSON representation of all the sticky notes

Now that we have the client side application running, we need to be adding in the support at the client side to convert all the sticky notes into a JSON representation which could then be sent to the server. The below javascript code includes the code for creating JSON representation as well as communicating with the server using WebSocket. If you are not familiar with WebSocket and its JavaEE 7 support then I would highly recommend you to read this post. The below javascript code contains the updated code along with the code provided above. The highlighted lines are the new additions to the code which deal with: Creating WebSocket connection to the server, Creating JSON representation of the data and sending the JSON representation to the server.

//File Name: notetoself.js
window.onload = init;

var websocket;
function init() {

  var button = document.getElementById("add_button");
  button.onclick = createSticky;

  //Loading all the sticky notes in the local storage and adding it to HTML DOM.
  var stickiesArray = getStickiesArray();
  for (var i = 0; i < stickiesArray.length; i++) {
    var key = stickiesArray[i];
    var value = localStorage.getItem(key);
    addStickyToDom(key, value);
  }

  var syncButton = document.getElementById("sync_button");
  syncButton.onclick = syncNotesToServer;
  initWebSocket();
}

/**
 * Function to init the WebSocket connection to the server endpoint
 */
function initWebSocket(){
  //If the websocket is not intialized, then create a new one.
  if (websocket == null) {
    websocket = new WebSocket("ws://localhost:8080/DemoApplication/saveStickNotes");
  }
  else if (websocket != null) {
    //The websocket might have been closed, so reconnect to the server.
    if (websocket.readyState != WebSocket.OPEN) {
      websocket = new WebSocket("ws://localhost:8080/DemoApplication/saveStickNotes");
    }
  }
}
/**
 * Function to sync the sticky notes to the server.
 */
function syncNotesToServer() {
  if (websocket != null && websocket.readyState == WebSocket.OPEN)
  {
    var stickiesArray = getStickiesArray();
    var stickyNotesArray = [];
    for (var i = 0; i < stickiesArray.length; i++) {
      var key = stickiesArray[i];
      var value = localStorage.getItem(key);
      stickyNotesArray.push({key: key, value: value});
    }
    console.log(JSON.stringify(stickyNotesArray));
    websocket.send(JSON.stringify(stickyNotesArray));
    var syncStatusDiv = document.getElementById("sync_status");
    syncStatusDiv.innerHTML = "Last sync to server: "+(new Date());
  }
}

/**
 * Event handler for the button which adds the notes.
 */
function createSticky() {
  var value = document.getElementById("note_text").value;
  var currentDate = new Date();
  var time = currentDate.getTime();

  //Creating the key for the sticky note
  var key = "sticky_" + time;
  //Storing the sticky note with the key in the local storage.
  localStorage.setItem(key, value);

  var stickiesArray = getStickiesArray();
  //Only the key is stored in the stickies array.
  stickiesArray.push(key);
  //And the array of sticky note keys is stored in local storage.
  localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));

  addStickyToDom(key, value);

}

/**
 * Function to add the sticky data to the HTML DOM.
 */
function addStickyToDom(key, value) {
  var stickies = document.getElementById("stickies");
  var sticky = document.createElement("li");
  sticky.setAttribute("id", key);
  var span = document.createElement("span");
  span.setAttribute("class", "sticky");
  span.innerHTML = value;
  sticky.appendChild(span);
  stickies.appendChild(sticky);
  sticky.onclick = deleteSticky;
}

/**
 * Function to load the sticky note data from the local storage.
 */
function getStickiesArray() {
  var stickiesArray = localStorage.getItem("stickiesArray");
  if (!stickiesArray) {
    stickiesArray = [];
    localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
  }
  else {
    stickiesArray = JSON.parse(stickiesArray);
  }

  return stickiesArray;
}
/**
 * Function to delete the sticky note from the local storage.
 */
function deleteSticky(e) {
  var key = e.target.id;
  if (e.target.tagName.toLowerCase() == "span") {
    key = e.target.parentNode.id;
  }
  localStorage.removeItem(key);
  var stickiesArray = getStickiesArray();
  if (stickiesArray) {
    for (var i = 0; i < stickiesArray.length; i++) {
      if (key == stickiesArray[i]) {
        stickiesArray.splice(i, 1);
      }
    }
    localStorage.setItem("stickiesArray", JSON.stringify(stickiesArray));
    removeStickyFromDom(key);
  }
}

/**
 * Function to remove the sticky note element present in the HTML DOM.
 */
function removeStickyFromDom(key) {
  var sticky = document.getElementById(key);
  sticky.parentNode.removeChild(sticky);
}

The JSON representation created by the above highlighted code is:

[{"key":"sticky_1381306898171","value":"how aout his"},{"key":"sticky_1381306902222","value":"Another one"},{"key":"sticky_1381309254661","value":"adding few more ..."}]

Now that we have the JSON data in place and also WebSocket client end point done, we will look at creating the WebSocket server end point and parsing the JSON data at the server.

Parsing the JSON data in the server

The JSR 353 adds support for JSON processing in Java. As I said in the beginning that in this post I will cover parsing JSON using Streaming API. The Streaming API is exactly similar to StAX parser for XML. The JSON parser goes through each line of the JSON data and generates different events depending on whether the data in the given line is START_ARRAY, END_ARRAY, START_OBJECT, END_OBJECT, KEY_NAME, VALUE_STRING, VALUE_NUMBER, VALUE_TRUE, VALUE_FALSE, VALUE_NULL. And we capture what ever event we are interested in and PULL the required data like the key name and the value against the key. If you are left wondering about JSON then I would HIGHLY recommend you read this post which explains in brief about JSON. The code below creates a web socket and then parses the JSON data:


import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
import javax.json.stream.JsonParser;
import static javax.json.stream.JsonParser.Event.END_OBJECT;
import static javax.json.stream.JsonParser.Event.KEY_NAME;
import static javax.json.stream.JsonParser.Event.VALUE_STRING;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

/**
 * Demo for parsing the JSON using Streaming API.
 */
@ServerEndpoint("/saveStickNotes")
public class TodoApplicationEndPoint {

  private Map<String, String> stickyNotes;

  @OnMessage
  public void receiveMessage(String message, Session session){
    parseUsingStreamingAPI(message);
  }

  private void parseUsingStreamingAPI(String message) {
    JsonParser parser = Json.createParser(new StringReader(message));
    String keyType = null;
    String key = null, value = null;
    stickyNotes = new HashMap<>();
    while(parser.hasNext()){
      //Identify the type of event i.e type of JSON element.
      switch (parser.next()){
        //If the JSON element is the "KEY", then record the key type.
        case KEY_NAME:
          keyType = parser.getString();
          break;
        //If the JSON element is a value string then extract the value
        //and decide if the value is a sticky note key or sticky note data.
        case VALUE_STRING:
          switch(keyType){
            case "key":
              key = parser.getString();
            case "value":
              value = parser.getString();
          }
          break;
        //Once we encounter the end of the sticky note object, update the
        //map with the sticky note key and sticky note value.
        case END_OBJECT:
          stickyNotes.put(key, value);
          break;
      }
    }
    //Printing the sticky note data to verify that we have correctly parsed the data.
    for(String k : stickyNotes.keySet()){
      System.out.println("Key: "+k+": "+ stickyNotes.get(k));
    }
  }
}

If you are confused or left wondering about Line 20, Line 25, Line 26 then I would highly recommend you to read the article about websockets. Then coming to the code in Line 31-58 it all deals with the JSON parsing. If you have worked with StAX parser before then this should all be very similar but if you haven’t then no issues I will try to explain it here. Let me annotate our JSON data with the different event types supported so that we can easily understand the above code:

[//START_ARRAY
  {//START_OBJECT
    "key"//KEY_NAME
    :
    "sticky_1381306898171",//VALUE_STRING

    "value"//KEY_NAME
    :
    "how aout his"//VALUE_STRING

  },//END_OBJECT

  {//START_OBJECT
    "key" //KEY_NAME
    :
    "sticky_1381306902222",//VALUE_STRING

    "value"//KEY_NAME
    :
    "Another one"//VALUE_STRING
  },//END_OBJECT

  {//START_OBJECT
    "key"//KEY_NAME
    :
    "sticky_1381309254661",//VALUE_STRING

    "value"//KEY_NAME
    :
    "adding few more ..."//VALUE_STRING

  }//END_OBJECT
]//END_ARRAY
  • Line 39 – We capture the event of encountering the name of the property or the key and store the name of the property into the variable keyType which is then used later.
  • Line 44-50 – We capture the event of encountering a String value. Now we make use of the name of the property captured in keyType to decide if the value which we have is either the “key” value or the “value” or “sticky note” value. And we according store the value in different variables namely key and value.
  • Line 54-56 – We capture the event of encountering the end of object. And once the we come to the end of the object we have by now captured the required data for 1 sticky note, the required data being the “key” and “value” of the sticky note and then update the Map with this “key” and “value”.

And when you click on the “Sync sticky notes to server” button you should be able to see the below output in your application server console:
2

This has been a really code heavy post. So if you are stuck somewhere then do drop in comments and I will try to help you out. But the code discussed in this post covers a lot of concepts and will be really helpful if you are able to grasp the complete code. I have uploaded the code to the Github repository so that you can easily refer to the code if you find any issues with the code provided here.

Comments

comments

About Mohamed Sanaulla

In his day job he works on developing enterprise applications using ADF. He is also the moderator of JavaRanch forums and an avid blogger.

Speak Your Mind

*