2

I'm working on a project to use JSON as a configuration framework for creating Java objects. This is also my first professional Java project coming from years of experience in CF/PHP/JS etc...

EVERY resource I can find on converting JSON to Java is predicated on the idea that you have to manually build the object in Java first, a POJO, and then use the JSON to populate it.

As a web language veteran, I'm choking on this idea. I get that compiled languages play differently, but I thought it was a tenet developers from command line to machine language shared: "If you have to do it more than twice, automate it."

...and yet it seems like the presiding Java wisdom is: Do the tedious structure building by hand, every time, then use GSON/Jackson/whatever to populate the rigid structure. If your JSON changes (and it will, because JSON) do the whole thing over again.

Is there a parallel to the web code way of thinking?
1) Load JSON
2) Build native language structure based on JSON structure.
3) Populate new object with data from structure.
4) Use object however you wish.

(As @Thomas pointed out, the most accurate scenario for why this would be necessary is a API response that returns JSON, it might be different periodically, and while duck typing is generally uncomfortable for high level languages, even converting EVERYTHING to a string saves effort and time compared to rebuilding your framework every time {or having a tool that converts your JSON from myriad sources to be usable by any other tool}. Again, it seems obvious from the perspective of cowboy languages, but so foreign to the high level languages people aren't understanding the question.)

8
  • 2
    If you don't want POJOs just use a Map. Otherwise, as Java is compiled, you need to define the structure of the incoming data. You can use a JSON schema, and compile that to POJOs. Commented Nov 3, 2016 at 16:58
  • 1
    Java developers are just happy to have GSON and Jackson for automatic JSON parsing. Java doesn't play well with "duck typing" it's just how it goes. Other high level languages are the same, i.e. C#, Swift, Objective-C, etc. Commented Nov 3, 2016 at 16:59
  • 2
    There are libraries which are able to create POJOs from JSON but you'd still have to somehow access the properties of those objects and without knowing them at compile time the only way to do this would be reflection - which then wouldn't provide much advantage (if any) over using maps. If your only problem is that properties might be added or removed then you could just configure the mapper to ignore those. Commented Nov 3, 2016 at 17:00
  • 1
    One lib to create pojos from json would be this: github.com/joelittlejohn/jsonschema2pojo - this works with a json schema or plain json (you'd have to make sure the plain json contains all properties though) Commented Nov 3, 2016 at 17:06
  • 1
    ... and yet it seems like the presiding Java wisdom is: Do the tedious structure building by hand, every time ... - I'd say that in the Java world you probably mostly encounter 2 situations: 1) you define an api in which case the pojo comes first (and the json schema/definition is created from it) or 2) you're working against some json api which already provides a pojo definition (or at least a json schema to generate pojos from). Most tutorials/sites you found are probably focusing on 1) since that's probably more common in the Java world as well as easier to explain. Commented Nov 3, 2016 at 17:12

1 Answer 1

2

I read carefully the comments above and I think there is a very common scenario where an ad hoc Java object would be extremely useful even if the API structure is known at compile time.

Lets say you deal with an API that return a complex JSON object in which you are only interested in a very specific property value.

Take as an example the folowing google map API call to get geo details givin an address:

https://maps.googleapis.com/maps/api/geocode/json?address=sunnyvale,CA

As you can check and see, the response structure is quite complex. Assuming you only want to get the lat and lng properties value, there should be an easier way rather than predefine the complete corresponding Java class.

So yes, if you don't want to go with the strongly typed predefined Java class, you will need to use raw Object type and Maps. More specifically, a StringMap.

And now finally I can give a solution:

Ideally, you would like to access the properties with the same native notation like you would do in JS. Something like this:

String url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address;
String responseStr = fetch(url);
JsonHelper response =  JsonHelper.forString(responseStr);

String status = (String) response.getValue("status");
if(status != null && status.equals("OK")) {
   lat = (Double) response.getValue("results[0].geometry.location.lat");        
   lng = (Double) response.getValue("results[0].geometry.location.lng");
}

The following JsonHelper class code is taken from the jello-framework and it lets you do exactly that.

package jello.common;

import java.util.List;

import com.google.gson.Gson;
import java.util.AbstractMap;

public class JsonHelper {

    private Object json;

    public JsonHelper(String jsonString) {
        Gson g = new Gson();
        json = g.fromJson(jsonString, Object.class);
    }

    public static JsonHelper forString(String jsonString) {
        return new JsonHelper(jsonString);
    }

    @SuppressWarnings("unchecked")
    public Object getValue(String path) {
        Object value = json;
        String [] elements = path.split("\\.");
        for(String element : elements) {
            String ename = element.split("\\[")[0];

            if(AbstractMap.class.isAssignableFrom(value.getClass())) {
                value = ( (AbstractMap<String, Object>) value).get(ename);

                if(element.contains("[")) {
                    if(List.class.isAssignableFrom(value.getClass())) {
                        Integer index = Integer.valueOf(element.substring(element.indexOf("[")+1, element.indexOf("]")) );
                        value = ((List<Object>) value).get(index);
                    }
                    else {
                        return null;
                    }
                }
            }
            else {
                return null;
            }
        }

        return value;
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Damn... that com.google.gson.internal package is a slippery fellow to find, and whats more I doubt my employer will be keen on my using it... but STILL... this is the solution I was looking for, even if I can't use it without reinventing the wheel.
Guess what, this is your lucky day. Your comment push me to revise my solution and I found that com.google.gson.internal.StringMap extends java.util.AbstractMap so I simply replaced the ugly internal class with the public interface to get a working elegant JSON helper. Please check my revised answer.
HAH! Nice. Thanks.
+1 to the question and the answer. Ahh....just loved the question (same situation as mine) and an elegant solution. Many Thanks !!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.