Rick

Rick
Rick

Tuesday, May 6, 2014

How to add Java JSON support without having 200 DTO objects or more annotations than code

If you have 75 annotations in a Java class and 25 more in the REST handler to do JSON than you are doing it wrong. If you have 500 DTO objects to manage every use case or every view then you are using JSON wrong.
Boon was invented after looking at what a SaaS company needed, and seeing what frameworks they were currently using. Boon was the reaction to an idea of let's have 500 DTOs to match each use case or view.
I could not handle writing 500 DTO objects for simple things, and I was sick of there being more annotations in my code then code, and class loaders issues, and , and … so Boon.
Let's say you have a large JSON stream / file / REST POST / Websocket call that looks like this (pretend what you see is large):

Sample JSON of teams

{

    "teamInfo" : {
        "teamRoster": {
            "teamNames": ["duck", "chickens", "penguins"]
        }

    }


} 
Sometimes you want to create a team info (TeamInfo). Sometimes you want just the list or Set. Sometimes you want just the team roster (TeamRoster). Different use cases use different parts of the JSON stream. Perhaps the JSON stream even gets put onto an event bus and many end points grab it and use different parts of the stream.

It is JSON so you should be able to add properties without breaking older event bus listeners.

Sample classes (DTO or domain objects).

    public static class TeamRoster {
        Set<String> teamNames;

        @Override
        public String toString() {
            return "TeamRoster{" +
                    "teamNames=" + teamNames +
                    '}';
        }
    }

    public static class TeamInfo {

        TeamRoster teamRoster;

        @Override
        public String toString() {
            return "TeamInfo{" +
                    "teamRoster=" + teamRoster +
                    '}';
        }
    }

In this example we will read the JSON with Boon from the classpath or file system withjsonResource (full example below).
        /* Using Boon style (easy) 2 parser. */
        jsonObject = Boon.jsonResource(path);

Now if we just want the team info part of the JSON we do this:
        /* Using Boon path. */
        puts ("teamInfo", atIndex(jsonObject, "teamInfo"));

(puts is like System.out.println but better)
teamInfo {teamRoster={teamNames=[duck, chickens, penguins]}} 
atIndex returns the teamInfo map.
You use atIndex to get just the part you care about. We can get the teamInfo, the teamRoster or the teamNames and different use cases (or different listeners on the bus might care about different things) so they can pick and choose what they want and ignore the rest. This is JSON. It is meant to be flexible.

Grabbing just the teamInfo.teamRoster

        puts("Team Roster", atIndex(jsonObject, "teamInfo.teamRoster"));

Grabbing just the teamInfo.teamRoster and teamInfo.teamRoster.teamNames

        puts("Team Roster", atIndex(jsonObject, "teamInfo.teamRoster"));
        puts("Team Names", atIndex(jsonObject, "teamInfo.teamRoster.teamNames"));

output

Team Roster {teamNames=[duck, chickens, penguins]} 
Team Names [duck, chickens, penguins] 
How do we convert team names into a Set?
Easy.
        List<String> teamNames = (List<String>) atIndex(jsonObject, "teamInfo.teamRoster.teamNames");

        puts("Team Names", teamNames);

        Set<String> teamNameSet = set(teamNames);

        puts ("Converted to a set", teamNameSet);
output
Team Names [duck, chickens, penguins] 
Converted to a set [duck, chickens, penguins] 
How do we convert a specific path into the TeamInfo domain object without converting the entire JSON stream?

Getting just the TeamInfo from JSON

        TeamInfo teamInfo = fromMap((Map<String, Object>) atIndex(jsonObject, "teamInfo"), TeamInfo.class);
        puts(teamInfo);

How do we convert a specific path into the TeamRoster domain object without converting the entire JSON stream?

Getting just the TeamRoster from JSON

        TeamRoster teamRoster = fromMap((Map<String, Object>) atIndex(jsonObject, "teamInfo.teamRoster"), TeamRoster.class);

Full example

/*
 */

package com.examples;

import org.boon.Boon;
import org.boon.IO;
import org.boon.json.JsonFactory;
import org.boon.json.JsonParserAndMapper;
import org.boon.json.JsonParserFactory;
import org.boon.json.ObjectMapper;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.boon.Boon.atIndex;
import static org.boon.Boon.puts;
import static org.boon.Maps.fromMap;
import static org.boon.Sets.set;

/**
 * Created by Richard on 5/6/14.
 */
public class PartialDataTreeExample {


    public static class TeamRoster {
        Set<String> teamNames;

        @Override
        public String toString() {
            return "TeamRoster{" +
                    "teamNames=" + teamNames +
                    '}';
        }
    }

    public static class TeamInfo {

        TeamRoster teamRoster;

        @Override
        public String toString() {
            return "TeamInfo{" +
                    "teamRoster=" + teamRoster +
                    '}';
        }
    }

    public static void main (String... args) {
        File file = new File(".", "src/test/resources/teams.json");
        String path = file.getAbsolutePath().toString();
        puts ("PATH", path);
        puts ("CONTENTS of PATH", IO.read(path));

        /* Jackson style interface. */
        ObjectMapper mapper = JsonFactory.create();
        Object jsonObject = mapper.readValue(file, Object.class);
        puts ("JSON Object", jsonObject);


        /* Using Boon path. */
        puts ("teamInfo", atIndex(jsonObject, "teamInfo"));
        puts("Team Roster", atIndex(jsonObject, "teamInfo.teamRoster"));
        puts("Team Names", atIndex(jsonObject, "teamInfo.teamRoster.teamNames"));


        /* Using Boon style parser (fast). */
        JsonParserAndMapper boonMapper = new JsonParserFactory().create();
        jsonObject = boonMapper.parseFile(path);


        /* Using Boon path. */
        puts ("teamInfo", atIndex(jsonObject, "teamInfo"));
        puts("Team Roster", atIndex(jsonObject, "teamInfo.teamRoster"));
        puts("Team Names", atIndex(jsonObject, "teamInfo.teamRoster.teamNames"));



        /* Using Boon style (easy) 2 parser. */
        jsonObject = Boon.jsonResource(path);


        /* Using Boon path. */
        puts ("teamInfo", atIndex(jsonObject, "teamInfo"));
        puts("Team Roster", atIndex(jsonObject, "teamInfo.teamRoster"));
        puts("Team Names", atIndex(jsonObject, "teamInfo.teamRoster.teamNames"));

        //There is also a Groovy style and a GSON style.

        List<String> teamNames = (List<String>) atIndex(jsonObject, "teamInfo.teamRoster.teamNames");

        puts("Team Names", teamNames);

        Set<String> teamNameSet = set(teamNames);

        puts ("Converted to a set", teamNameSet);


        TeamInfo teamInfo = fromMap((Map<String, Object>) atIndex(jsonObject, "teamInfo"), TeamInfo.class);
        puts(teamInfo);


        TeamRoster teamRoster = fromMap((Map<String, Object>) atIndex(jsonObject, "teamInfo.teamRoster"), TeamRoster.class);
        puts(teamRoster);

    }

}
Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training