0

this is my first time making an external api call in Java, so please bear with me as I'm not very experienced. I got the http request working and got a response, but now I need to parse it.

I'm trying to convert a json array to java objects. I understand the gist of it, but all examples I've seen don't apply to my issue.

I need the 'entities' objects from the json string. The details (which are an array, too) can contain any key/value pair, so I was thinking of putting that in a hashmap in each Entity object. I've tried the gson library, but I can't find any gson example that goes deeper than a single dimensional json array.

I realize this is kind of a broad question, and I don't expect anyone to deliver me a working solution, but a few tips or a link to a relevant guide would go a long way. :)

{
   "return": {
      "entities": [
         {
            "id": 2385,
            "details": [
               {
                  "name": "Other Known Name",
                  "value": "John Wick",
                  "match": false
               }
            ],
            "proofs": [],
            "link": "http://domain.gg/users?id=2385"
         },
         {
            "id": 2384,
            "details": [
               {
                  "name": "Discord ID",
                  "value": "159985870458322944",
                  "match": false
               },
               {
                  "name": "SteamID64",
                  "value": "76561197991558078",
                  "match": true
               },
               {
                  "name": "SteamVanity",
                  "value": "test",
                  "match": false
               },
               {
                  "name": "PS4",
                  "value": "John_S",
                  "match": false
               },
               {
                  "name": "XBox",
                  "value": "John S",
                  "match": false
               },
               {
                  "name": "Email",
                  "value": "john_smith@gmail.com",
                  "match": true
               },
               {
                  "name": "Comment",
                  "value": "Test user",
                  "match": false
               },
               {
                  "name": "Other Known Name",
                  "value": "Jonathan",
                  "match": false
               },
               {
                  "name": "Reddit",
                  "value": "/u/johns",
                  "match": true
               }
            ],
            "proofs": [],
            "link": "http://domain.gg/users?id=2384"
         },
         {
            "id": 1680,
            "details": [
               {
                  "name": "Other Known Name",
                  "value": "Johny",
                  "match": false
               },
               {
                  "name": "SteamID64",
                  "value": "76561198213003675",
                  "match": true
               }
            ],
            "proofs": [],
            "link": "http://domain.gg/users?id=1680"
         },
         {
            "id": 1689,
            "details": [
               {
                  "name": "Other Known Name",
                  "value": "JohnnyPeto",
                  "match": false
               },
               {
                  "name": "SteamID64",
                  "value": "76561198094228192",
                  "match": true
               }
            ],
            "proofs": [],
            "link": "http://domain.gg/users?id=1689"
         }
      ],
      "notice": "Showing 4 out of 4 matches."
   }
}
RoboticR
  • 121
  • 2
  • 6
  • "The details (which are an array, too) can contain any key/value pair". Are you sure this is true? I would expect any half-decent API to adhere to a specification. Otherwise how would you know what to expect? If I'm right, I would find the specification and create a Java object to model the response you receive. – Michael Mar 30 '17 at 12:32
  • _... but I can't find any gson example that goes deeper than a single dimensional json array_ -- So you even didn't try to make even a tiny experiment? – Lyubomyr Shaydariv Mar 30 '17 at 12:38
  • Look at this question: http://stackoverflow.com/questions/15268594/iterate-through-nested-json-object-array I think it will help. –  Mar 30 '17 at 12:39

4 Answers4

1

There are many json serialization/deserialization frameworks available. I would recommend having a look at Jackson.

Basically, you have to create Model corresponding to json schema and deserialize json into object. Based on the example in the question, model will look like this:

@JsonIgnoreProperties(ignoreUnknown = true)
class Response {

    @JsonProperty("return")
    private ResponseObject responseObject;

    public ResponseObject getResponseObject() {
        return responseObject;
    }

    public void setResponseObject(ResponseObject responseObject) {
        this.responseObject = responseObject;
    }
}

@JsonIgnoreProperties(ignoreUnknown = true)
class ResponseObject {
    private List<Entity> entities;

    public List<Entity> getEntities() {
        return entities;
    }

    public void setEntities(List<Entity> entities) {
        this.entities = entities;
    }


}   

@JsonIgnoreProperties(ignoreUnknown = true)
class Entity {
    private String id;
    private List<Details> details;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public List<Details> getDetails() {
        return details;
    }

    public void setDetails(List<Details> details) {
        this.details = details;
    }

}

@JsonIgnoreProperties(ignoreUnknown = true)
class Details {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Once the model is defined, you can use ObjectMapper class to perform serialization/deserialization, e.g.:

ObjectMapper mapper = new ObjectMapper();
Response response = mapper.readValue("{\"return\": {\"entities\": [{\"id\": 2385,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"John Wick\",\"match\": false}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=2385\"},{\"id\": 2384,\"details\": [{\"name\": \"Discord ID\",\"value\": \"159985870458322944\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561197991558078\",\"match\": true},{\"name\": \"SteamVanity\",\"value\": \"test\",\"match\": false},{\"name\": \"PS4\",\"value\": \"John_S\",\"match\": false},{\"name\": \"XBox\",\"value\": \"John S\",\"match\": false},{\"name\": \"Email\",\"value\": \"john_smith@gmail.com\",\"match\": true},{\"name\": \"Comment\",\"value\": \"Test user\",\"match\": false},{\"name\": \"Other Known Name\",\"value\": \"Jonathan\",\"match\": false},{\"name\": \"Reddit\",\"value\": \"/u/johns\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=2384\"},{\"id\": 1680,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"Johny\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561198213003675\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=1680\"},{\"id\": 1689,\"details\": [{\"name\": \"Other Known Name\",\"value\": \"JohnnyPeto\",\"match\": false},{\"name\": \"SteamID64\",\"value\": \"76561198094228192\",\"match\": true}],\"proofs\": [],\"link\": \"http://domain.gg/users?id=1689\"}],\"notice\": \"Showing 4 out of 4 matches.\"}}", Response.class);
System.out.println(response.getResponseObject().getEntities().get(0).getId());

Here's the Javadoc.

Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
0

If I were you, I'd use Jackson, not GSON. It's specialized on JavaBeans-style mapping. Write classes like this:

public class Detail{
    private String name;
    private String value;
    private boolean match;
    // + getters / setters
}

public class Entity{
    private int id;
    private List<Detail> details;
    private String link;
    private List<String> proofs;
    // you don't have any example data for this, so I'm assuming strings
    // + getters / setters
}

public class Result{
    private List<Entity> entities;
    private String notice;
    // + getters / setters
}

and do the conversion with something like

Result result = new ObjectMapper().readValue(json, Result.class);
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
0

As my fellow stackoverflow users have previously posted, for this kind of initilization Jackson API would be better. I have however posted the solution for your question with Gson.

I noticed that you like your details to be stored as a HashMap with id as key. However, it seems like this id is actually related to the entities and not to the details.

Disclaimer, I got lazy and used an online POJO generator because I did not want to create objects for all of the Json elements ;) It still showcases how it should be done:

class Main{

public static void main(String[] args) throws FileNotFoundException {
//this is just to load the json file
    String input = new Scanner(new File("test.txt")).useDelimiter("\\Z").next();

    System.out.println(input);
    Gson gson = new Gson();
    Example arr = gson.fromJson(input, Example.class);

    System.out.println(arr);
}

public class Detail {

    @SerializedName("name")
    @Expose
    public String name;
    @SerializedName("value")
    @Expose
    public String value;
    @SerializedName("match")
    @Expose
    public Boolean match;
    @Override
    public String toString() {
        return "Detail [name=" + name + ", value=" + value + ", match=" + match + "]";
    }

    }

public class Entity {

    @SerializedName("id")
    @Expose
    public Integer id;
    @SerializedName("details")
    @Expose
    public List<Detail> details = null;
    @SerializedName("proofs")
    @Expose
    public List<Object> proofs = null;
    @SerializedName("link")
    @Expose
    public String link;
    @Override
    public String toString() {
        return "Entity [id=" + id + ", details=" + details + ", proofs=" + proofs + ", link=" + link + "]";
    }

    }

public class Example {

    @SerializedName("return")
    @Expose
    public Return _return;

    @Override
    public String toString() {
        return "Example [_return=" + _return + "]";
    }

    }
public class Return {

    @SerializedName("entities")
    @Expose
    public List<Entity> entities = null;
    @SerializedName("notice")
    @Expose
    public String notice;
    @Override
    public String toString() {
        return "Return [entities=" + entities + ", notice=" + notice + "]";
    }

    }
}

Output

Example [_return=Return [entities=[Entity [id=2385, details=[Detail [name=Other Known Name, value=John Wick, match=false]], proofs=[], link=http://domain.gg/users?id=2385], Entity [id=2384, details=[Detail [name=Discord ID, value=159985870458322944, match=false], Detail [name=SteamID64, value=76561197991558078, match=true], Detail [name=SteamVanity, value=test, match=false], Detail [name=PS4, value=John_S, match=false], Detail [name=XBox, value=John S, match=false], Detail [name=Email, value=john_smith@gmail.com, match=true], Detail [name=Comment, value=Test user, match=false], Detail [name=Other Known Name, value=Jonathan, match=false], Detail [name=Reddit, value=/u/johns, match=true]], proofs=[], link=http://domain.gg/users?id=2384], Entity [id=1680, details=[Detail [name=Other Known Name, value=Johny, match=false], Detail [name=SteamID64, value=76561198213003675, match=true]], proofs=[], link=http://domain.gg/users?id=1680], Entity [id=1689, details=[Detail [name=Other Known Name, value=JohnnyPeto, match=false], Detail [name=SteamID64, value=76561198094228192, match=true]], proofs=[], link=http://domain.gg/users?id=1689]], notice=Showing 4 out of 4 matches.]]
kkflf
  • 2,435
  • 3
  • 26
  • 42
0

Despite there are answers suggesting you to use Jackson, you can still accomplish easily with Gson with its default configuration just creating proper relations between mappings:

// A generic response, parameterized with <T>, can hold any type except of primitives
final class Response<T> {

    @SerializedName("return")
    final T ret = null;

}

final class EntitiesAndNotice {

    final List<Entity> entities = null;
    final String notice = null;

}

final class Entity {

    // Unlike Object and any its subclasses, `int` being a primitive cannot be nulled
    // Simple 0 won't work either, because the compiler will inline it
    // So it's a sort of cheating javac to return a value that holds 0 already
    final int id = Integer.valueOf(0);
    final List<Detail> details = null;
    // Your JSON document does not provide enough info on the elements type
    // So it depends on how Gson parses JSON tokens
    final List<Object> proofs = null;
    final URL link = null;

}

final class Detail {

    final String name = null;
    final String value = null;
    // The same for primitive booleans, or Boolean.FALSE
    final boolean match = Boolean.valueOf(false);

}

Example use:

private static final String JSON = "{\"return\":{\"entities\":[{\"id\":2385,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"John Wick\",\"match\":false}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=2385\"},{\"id\":2384,\"details\":[{\"name\":\"Discord ID\",\"value\":\"159985870458322944\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561197991558078\",\"match\":true},{\"name\":\"SteamVanity\",\"value\":\"test\",\"match\":false},{\"name\":\"PS4\",\"value\":\"John_S\",\"match\":false},{\"name\":\"XBox\",\"value\":\"John S\",\"match\":false},{\"name\":\"Email\",\"value\":\"john_smith@gmail.com\",\"match\":true},{\"name\":\"Comment\",\"value\":\"Test user\",\"match\":false},{\"name\":\"Other Known Name\",\"value\":\"Jonathan\",\"match\":false},{\"name\":\"Reddit\",\"value\":\"/u/johns\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=2384\"},{\"id\":1680,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"Johny\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561198213003675\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=1680\"},{\"id\":1689,\"details\":[{\"name\":\"Other Known Name\",\"value\":\"JohnnyPeto\",\"match\":false},{\"name\":\"SteamID64\",\"value\":\"76561198094228192\",\"match\":true}],\"proofs\":[],\"link\":\"http://domain.gg/users?id=1689\"}],\"notice\":\"Showing 4 out of 4 matches.\"}}";

private static final Gson gson = new Gson();

private static final TypeToken<Response<EntitiesAndNotice>> responseTypeToken = new TypeToken<Response<EntitiesAndNotice>>() {
};

public static void main(final String... args) {
    final Response<EntitiesAndNotice> response = gson.fromJson(JSON, responseTypeToken.getType());
    final String value = response.ret.entities.get(1).details.get(3).value;
    System.out.println(value);
}

Output:

John_S

Lyubomyr Shaydariv
  • 20,327
  • 12
  • 64
  • 105