Friday, March 22, 2013

JQuery, JSON, and Java Servlets...

So my blog has gotten a bit dry lately. It has to do with my new job: there's some flashy stuff, but less than at my old gig, and I'm having to learn new technologies, some of which are a little dull.

Anyway, a while back I wrote about a Node.js-based raw "POST" echo tool I made because I was having some troubles do "real POSTs". Well, like they say, you know what 'experience' is? It's what lets you recognize a mistake when you make it a second time.

So, future self, if you really want to POST raw JSON from jQuery (and it might not be that important to, for a lot of stuff the x-www-form-urlencoded stuff works fine), you need to make sure your jQuery ajax looks something like:
    $.ajax
    ({
        type: "POST",

        url: 'infoservlet',
        dataType: "json",
        contentType:"application/json",

        data: JSON.stringify({'abc':['defg','ghij']}),
        success: showGoodResult,
        failure: showBadResult
    });

The bits in red are what I tend to forget.

So I'm also writing the java side of things, which again is something I haven't done in a while. Servlets are a much older technology than JSON, and sometimes they feel like it, so getting the two to play nicely is not quite as graceful as it should be.

My recommendation for the Java JSON is JSON-simple (more on that below). It uses JSONObject for the maps and JSONArray for arrays and that's 90% of what you need. 

So apologies for the following code, its been a long day, and I'm a little goofy when I write my "teach myself" stuff...

To show that I could output JSON Jquery could read I did 
     protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
            resp.setContentType("application/json");
            outputTestPage(new PrintStream(resp.getOutputStream()), "TEST");
    }

 private void outputTestPage(PrintStream out, String result) {
        out.println(getStuff(result));
    }
    private Object getStuff(String s){
        JSONObject obj=new JSONObject();
        obj.put("name",s);
        return obj;
    }


And indeed the jQuery ajax success function got a JSON result with "name" as a key and "TEST" as a value. 

To read the POST the above jQuery made, I found RawSushi's guide to getting the POST body and then did something like this:

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        BufferedReader reader = req.getReader();
        StringBuilder inputStringBuilder = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            inputStringBuilder.append(line);
        }
        resp.setContentType("application/json");
        String s = inputStringBuilder.toString();
        Object obj=JSONValue.parse(s);
        JSONObject stuff=(JSONObject)obj;
        JSONArray ja = (JSONArray) stuff.get("abc");
       outputTestPage(new PrintStream(resp.getOutputStream()),  

           ja.get(0).toString() 
             + " and then "+ja.get(1).toString());
    }   


That was then able to show me the "defg" and then "ghij" values I had passed in when I POSTed.

So another contender for the Java JSON handling was google-gson. My first reaction to it was kind of negative... it reminded me of old XML marshallers I had used in the past... it was strongly geared at mapping Java Bean-style objects, with named fields. But because I am trying to be more flexible, I started to give it a go, and it wasn't terrible. But then I thought about WHY I didn't like the philosophy of it... was I just too used to the squirrely world of Javascript? Well, yes. Do I have a strong preference for a failure to look like "huh, there's no value for 'key'" rather than "huh, the marshalling process broke somehow, maybe you're missing one of the key values"? Also Yes! But then I really managed to justify my dislike: this kind of system is highly coupled. Change your data object and the client code has to change with it, or things break. It also assumes a 1-to-1 mapping to the input coming in to make the object and those fields... so if one of those fields were a compound of two different inputs, you'd be out of luck. So you end up with a pile of beans, either special beans just for the communication marshalling, or your "real work" data beans that you can't change because you don't want to break the client. Bleh!

3 comments:

  1. I have settled on Jackson's object mapper at work (configured with one line to ignore "missing" fields). It's fast, and it works great with POJOs.

    ReplyDelete
  2. Yeah, that might be a good bet if I go down the "expose my data objects to the outside world" road...

    ReplyDelete
  3. var point=["1","2","3","3"];
    console.log(URL);
    //var ajson = [1,2,3,4];
    console.log('hi');
    $.ajax({
    type: 'POST',
    url: URL,
    contentType:"application/json",
    data: JSON.stringify({point:point}),
    dataType: 'json',
    success: function(result) {
    console.log(result);
    //jQuery('#'+dataDiv).html(html);
    },
    error: function(e){
    alert('Error in Processing');
    }

    });


    getting at this line JSONArray ja = (JSONArray) stuff.get("point");

    java.lang.String Cannot be cast to java.json.JSONArray

    ReplyDelete