0

I have set the following payload into Gson's JsonObject:

 {
        "backup": {
            "addresses": [
                "127.0.0.1"
            ], 
            "healthcheck_interval": "60", 
            "http": "https", 
            "port": "9001"
        }, 
        "cognito": {
            "addresses": [
                "127.0.0.1"
            ], 
            "http": "https", 
            "port": "9012"
        }, 
        "collector": {
            "addresses": [
                "127.0.0.1"
            ], 
            "healthcheck_interval": "60", 
            "http": "https", 
            "port": "9000"
        }, 
        "ds": {
            "addresses": [
                "127.0.0.1"
            ], 
            "http": "https", 
            "port": "9011"
        }, 
        "insight-analytics": {
            "addresses": [
                "127.0.0.1"
            ], 
            "http": "https", 
            "port": "9013"
        }, 
        "integrations": {
            "addresses": [
                "127.0.0.1"
            ], 
            "http": "https", 
            "port": "9014"
        }, 
        "server": {
            "addresses": [
                "127.0.0.1"
            ], 
            "healthcheck_interval": "60", 
            "http": "https", 
            "port": "9009"
        }, 
        "vigile": {
            "addresses": [
                "127.0.0.1"
            ], 
            "http": "https", 
            "port": "9443"
        }, 
        "walt": {
            "addresses": [
                "127.0.0.1"
            ], 
            "http": "https", 
            "port": "9010"
        }
    }

Test:

@Test(priority = 10)
public void parseServicesFileToGsonObject() {
 JsonParser jsonParser = new JsonParser();
 JsonElement rootNode = jsonParser.parse(response);
 if (rootNode.isJsonObject()) {
  JsonObject details = rootNode.getAsJsonObject();
 }
}

I want to be able to store all the members of the JSON into string array (backup, cognito, collector , etc) in a manner that I can find the object using the string array to get further values (such as healthcheck_interval, http, port, etc). I tried using streams for this cause with no success. I wanna do this since it is quite possible that in the future names of the field could be changed (instead of backup it could be changed to another name).

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
tupac shakur
  • 658
  • 1
  • 12
  • 29

2 Answers2

1

You need to rethink your problem. The good thing about GSON is that it's able to reflectively serialize and unserialize JSON from actual Java objects.

This means that you should first define your object and then let GSON do the dirty work, eg:

class Server
{
  List<String> addresses;
  int healthcheck_interval;
  String http;
  int port;
}

so that you can then have

GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();

List<Server> servers = gson.fromJson(json, 
  new TypeToken<List<Server>>(){}.getType()
);
Jack
  • 131,802
  • 30
  • 241
  • 343
  • I tried your approach and I got the following error: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $ – tupac shakur Mar 07 '19 at 15:16
  • because your json file is a map, not a list, you should unserialize to `Map`, check this answer to see the slight difference https://stackoverflow.com/a/12117517/121747 – Jack Mar 07 '19 at 15:43
  • I checked the answer you suggested but using this will lose my json object root names ("backup", "collector", etc) - I want to be able to save all the root names into a list and by that when searching inside the json object I'll get the relevant one. – tupac shakur Mar 07 '19 at 15:54
  • You won't lose them, they'll become keys in the map, so that your `Map.Entry` will hold them. I think you need to properly know the language and the SDK before attempting more complicated things. – Jack Mar 07 '19 at 15:55
  • Having saying that the result that you suggested will hold the root object names as keys and port, http,etc will be its values? – tupac shakur Mar 07 '19 at 16:15
1

The most generic way is to use Map<String, Object> to represent dynamic structure of JSON payload. In that case, we can always use Map#keySet() method to list all properties and we do not need any String[] to keep them. Also, lookup objects by key is faster than using array or List. If our object values have constant structure we can map it to POJO to have stable access to values in runtime. Below code shows both cases:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Gson gson = new GsonBuilder().create();

        System.out.println("Use generic Map");
        Type genericMapType = new TypeToken<Map<String, Object>>() {
        }.getType();
        Map<String, Object> map = gson.fromJson(new FileReader(jsonFile), genericMapType);
        map.forEach((k, v) -> System.out.println(k + " => " + v));

        System.out.println();
        System.out.println();

        System.out.println("Use Service Map");
        Type serviceMapType = new TypeToken<Map<String, Service>>() {
        }.getType();
        Map<String, Service> serviceMap = gson.fromJson(new FileReader(jsonFile), serviceMapType);
        serviceMap.forEach((k, v) -> System.out.println(k + " => " + v));
    }
}

class Service {
    private List<String> addresses;

    @SerializedName("healthcheck_interval")
    private int healthCheckInterval;
    private String http;
    private int port;

    public List<String> getAddresses() {
        return addresses;
    }

    public void setAddresses(List<String> addresses) {
        this.addresses = addresses;
    }

    public int getHealthCheckInterval() {
        return healthCheckInterval;
    }

    public void setHealthCheckInterval(int healthCheckInterval) {
        this.healthCheckInterval = healthCheckInterval;
    }

    public String getHttp() {
        return http;
    }

    public void setHttp(String http) {
        this.http = http;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    @Override
    public String toString() {
        return "Service{" +
                "addresses=" + addresses +
                ", healthCheckInterval=" + healthCheckInterval +
                ", http='" + http + '\'' +
                ", port=" + port +
                '}';
    }
}

Above code prints:

Use generic Map
backup => {addresses=[127.0.0.1], healthcheck_interval=60, http=https, port=9001}
cognito => {addresses=[127.0.0.1], http=https, port=9012}
collector => {addresses=[127.0.0.1], healthcheck_interval=60, http=https, port=9000}
ds => {addresses=[127.0.0.1], http=https, port=9011}
insight-analytics => {addresses=[127.0.0.1], http=https, port=9013}
integrations => {addresses=[127.0.0.1], http=https, port=9014}
server => {addresses=[127.0.0.1], healthcheck_interval=60, http=https, port=9009}
vigile => {addresses=[127.0.0.1], http=https, port=9443}
walt => {addresses=[127.0.0.1], http=https, port=9010}


Use Service Map
backup => Service{addresses=[127.0.0.1], healthCheckInterval=60, http='https', port=9001}
cognito => Service{addresses=[127.0.0.1], healthCheckInterval=0, http='https', port=9012}
collector => Service{addresses=[127.0.0.1], healthCheckInterval=60, http='https', port=9000}
ds => Service{addresses=[127.0.0.1], healthCheckInterval=0, http='https', port=9011}
insight-analytics => Service{addresses=[127.0.0.1], healthCheckInterval=0, http='https', port=9013}
integrations => Service{addresses=[127.0.0.1], healthCheckInterval=0, http='https', port=9014}
server => Service{addresses=[127.0.0.1], healthCheckInterval=60, http='https', port=9009}
vigile => Service{addresses=[127.0.0.1], healthCheckInterval=0, http='https', port=9443}
walt => Service{addresses=[127.0.0.1], healthCheckInterval=0, http='https', port=9010}
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • 1
    Ziober thanks for your answer , I have another question: if I want to randomly select key, value item from the map how can I do that? – tupac shakur Mar 10 '19 at 08:53
  • @tupacshakur, no problem. For random values see [Is there a way to get the value of a HashMap randomly in Java?](https://stackoverflow.com/questions/929554/is-there-a-way-to-get-the-value-of-a-hashmap-randomly-in-java) – Michał Ziober Mar 10 '19 at 12:21