Given the following JSON source
{
"database": "Accounts",
"groups": {
"databaseDictionary": {
"folder": "C:\\Development\\Work\\Ac\\Data\\DataDictionary"
},
"sqlCreateDatabase": {
"name": "Sleaford",
"user": "JBarrow",
"useWindowsAuthentication": "true"
},
"defaultData": {
"folder": "C:\\Development\\Work\\Ac\\Data\\Configuration"
}
}
}
I was hoping that the following class structure would successfully populate the object.
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
public class JsonNestedHashMapConcise {
private Configuration data;
private class Configuration {
private String database;
private HashMap<String, Group> groups;
private class Group {
private HashMap<String, String> settings;
}
}
public JsonNestedHashMapConcise (String fn) {
try (Reader jsonFile = new BufferedReader(new FileReader(new File(fn)))) {
Gson jsonParser = new Gson();
this.data = jsonParser.fromJson(jsonFile, Configuration.class);
}
catch (JsonSyntaxException | IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JsonNestedHashMapConcise myConfiguration;
myConfiguration = new JsonNestedHashMapConcise("C:\\StackOverflow\\Concise.json");
System.out.println("Completed reading database configuration for " + myConfiguration);
}
}
However, while the Configuration.groups HashMap is populated, the nested groups.settings HashMap is null.
I have searched around and found possible solutions around techniques such as JsonDeserializer, registerTypeAdapter & TypeToken but I can’t comprehend how they fit into solving my problem.
As a bit of background, I have a starting point (source and sample JSON listed below) that provides a workaround but requires a more verbose JSON syntax. It was while writing the supporting methods I spotted that if I make the Configuration.groups field a HashMap, it would lead to more efficient queries. I have also read that inner classes were supported in Gson and this starting point appears to support that statement.
JSON source for verbose structure
{
"database": "Accounts",
"groups": [
{
"name": "databaseDictionary",
"settings": {
"folder": "C:\\Development\\Work\\Ac\\Data\\DataDictionary"
}
},{
"name": "sqlCreateDatabase",
"settings": {
"name": "Sleaford",
"user": "JBarrow",
"useWindowsAuthentication": "true"
}
},{
"name": "defaultData",
"settings": {
"folder": "C:\\Development\\Work\\Ac\\Data\\Configuration"
}
}
]
}
works with the following class structure
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
public class JsonNestedHashMapVerbose {
private Configuration data;
private class Configuration {
private String database;
private ArrayList<Group> groups;
private class Group {
private String name;
private HashMap<String, String> settings;
}
}
public JsonNestedHashMapVerbose (String fn) {
try (Reader jsonFile = new BufferedReader(new FileReader(new File(fn)))) {
Gson jsonParser = new Gson();
this.data = jsonParser.fromJson(jsonFile, Configuration.class);
}
catch (JsonSyntaxException | IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
JsonNestedHashMapVerbose myConfiguration;
myConfiguration = new JsonNestedHashMapVerbose("C:\\StackOverflow\\Concise.json");
System.out.println("Completed reading database configuration for " + myConfiguration);
}
}
The only difference I can spot between the two designs is that the starting structure has an explicit ‘settings’ element defined within the JSON which may be needed for the parser to be able to deserialize that part of the structure.
Am I pushing the Gson deserializer too far or is there some additional code I can add to help Gson complete the task?
Feedback on suggested duplicate question
The solution detailed in the suggested related question, using a parser provided by creates a very verbose class structure which I have detailed below (edited for clarity).
This automated parser method doesn't recognise that these related elements (databaseDictionary, sqlCreateDatabase & defaultData) can all be stored in a HashMap structure, and I would have been very impressed if the parser could have detected that. Instead, we end up with a class for each and every element.
public class DatabaseDictionary {
public String folder;
}
public class DefaultData {
public String folder;
}
public class Example {
public String database;
public Groups groups;
}
public class Groups {
public DatabaseDictionary databaseDictionary;
public SqlCreateDatabase sqlCreateDatabase;
public DefaultData defaultData;
}
public class SqlCreateDatabase {
public String name;
public String user;
public String useWindowsAuthentication;
}
The JSON source detailed at the start of this question is a subset of the number of elements existing within the groups element and these will be expanded (and later searched on).
Using the class structure detailed using would require that I create a new class to support each and every element (and I expect there to be 10s or 100s of these elements within the source). Using this structure would have also meant that it was very hard to locate a specified group element within the groups element to extract its attributes.
I can parse the JSON source using the primitive methodology of JsonToken to populate the java class
private class Configuration {
private String database;
private HashMap<String, Group> groups;
private class Group {
private HashMap<String, String> settings;
}
}
but, given that the GSON parser clearly can detect and populate HashMap elements as illustrated in the second example, I had hoped that a two tier HashMap would be detected in the same manner to avoid the repeated use of JsonToken as I have other similar JSON structures that would benefit from the use of HashMap.
I don't know whether the GSON parser detected the class type in the supplied java class or it detected that there were multiple element names at the same level in order to populate a single HashMap as I don't have a knowledge of the internal workings of GSON.
I was hoping that there was a supporting GSON abstract method that I could use to educate the parser into populating the hierarchical HashMap class.
In case anyone is interested
If anyone else wants to model a similar structure, I found that using the JsonReader class led to quite a readable solution even though it would be nice if the toJson method would have just worked as minimalist code to write. I have provided my source below, although as I am still a bit of a novice, I am sure that there may be more elegant ways to write some of the methods.
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
public class DatabaseConfiguration {
private String database;
private HashMap<String, Group> groups;
private class Group {
private HashMap<String, String> settings;
}
private Group ReadGroup(JsonReader jsonReader) throws IOException {
Group result = null;
// Iterate over the the list of group attributes
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (result == null) {
result = new Group();
result.settings = new HashMap<>();
}
result.settings.put(name, jsonReader.nextString());
}
jsonReader.endObject();
return result;
}
private HashMap<String, Group> ReadGroups (JsonReader jsonReader) throws IOException {
HashMap<String, Group> result = null;
// Iterate over the the list of groups
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
Group myGroup;
myGroup = ReadGroup(jsonReader);
if (result == null) {
result = new HashMap<>();
}
result.put(name, myGroup);
}
jsonReader.endObject();
return result;
}
public DatabaseConfiguration (String fn) {
try (Reader jsonFile = new BufferedReader(new FileReader(new File(fn)));
JsonReader jsonReader = new JsonReader(jsonFile)) {
// Root node (database + groups)
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
switch (name) {
case "database":
this.database = jsonReader.nextString();
break;
case "groups":
this.groups = ReadGroups(jsonReader);
break;
default:
throw new JsonSyntaxException("Unexpected name " + name + "in outer element of " + fn);
}
}
jsonReader.endObject();
}
catch (JsonSyntaxException | IOException e) {
System.out.println(e);
}
}
public static void main(String[] args) {
DatabaseConfiguration myConfiguration;
myConfiguration = new DatabaseConfiguration("C:\\StackOverflow\\Concise.json");
System.out.println("Completed reading database configuration for " + myConfiguration);
}
}
An aside as my first time posting here
My original question was originally marked as a duplicate and so was automatically closed. I then, as advised, edited my question to detail why the suggested solution in the duplicate question was not related to my question. I then expected the question to be re-opened (as it had been edited) but this never happened. I didn't want to cut-and-paste my edited question to re-issue it as I felt that would be abusing the principles behind Stack Overflow and so I leave confused as to what the correct methodology is to successfully retract a question incorrectly marked as a duplicate.
Since I commented above, I have now discovered (by accident) that I can ask the originator of the 'duplicate' marker to review their decision by 'pinging' them in a comment so I will now try that!
It would be nice if there was an obvious option after narrating why the original question isn't a duplicate on the form but as I am a new user, I can't post any feedback in the general forum until I have 5 credits!
Related
I have a simple class in java like below:
class Simple {
private String name;
private String email;
}
I want to have behaviour of java.util.List<Simple> and Simple both according to input data that my program receives.
i.e.
Case 1::
if my program receives below kind of json-array input
{"simple" : [ {"name":"a", "email" : "a#z.com"}, {"name":"b", "email" : "b#z.com"} ]}
I need to parse it using List<Simple>
Case 2::
if my program receives below kind of json-object input
{"simple" : {"name":"c", "email" : "c#z.com"} }
I need to parse it using Simple
Note: I have tried using JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, but the problem is it is basically converting single value also into json-array at the time of writing json.
I need to persist json as it is, is there any other way to achieve this?
To avoid any Jackson customisation I would create wrapper class with an Object simple property. We can add two extra checking methods and two extra casting methods. It will allow Jackson to do it's logic and in runtime we can check what actually we have:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
public class DateApp {
private final static JsonMapper JSON_MAPPER = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
public static void main(String[] args) throws Exception {
Simple object = new Simple("John", "john#doe.com");
SimpleWrapper wrapper = new SimpleWrapper();
wrapper.setSimple(object);
serializeAndDeserialize(wrapper);
wrapper.setSimple(Collections.singletonList(object));
serializeAndDeserialize(wrapper);
}
private static void serializeAndDeserialize(SimpleWrapper wrapper) throws JsonProcessingException {
String json = JSON_MAPPER.writeValueAsString(wrapper);
System.out.println("JSON:");
System.out.println(json);
wrapper = JSON_MAPPER.readValue(json, SimpleWrapper.class);
System.out.println("Wrapper:");
System.out.println(wrapper);
}
}
#Data
class SimpleWrapper {
private Object simple;
#JsonIgnore
public boolean isSimpleObject() {
return simple instanceof Simple;
}
#JsonIgnore
public boolean isSimpleList() {
return simple instanceof List;
}
#JsonIgnore
public Simple getSimpleAsObject() {
return (Simple) simple;
}
#JsonIgnore
public List<Simple> getSimpleAsList() {
return (List<Simple>) simple;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Simple {
private String name;
private String email;
}
Above code prints:
JSON:
{
"simple" : {
"name" : "John",
"email" : "john#doe.com"
}
}
Wrapper:
SimpleWrapper(simple={name=John, email=john#doe.com})
JSON:
{
"simple" : [ {
"name" : "John",
"email" : "john#doe.com"
} ]
}
Wrapper:
SimpleWrapper(simple=[{name=John, email=john#doe.com}])
You can use JsonNode.isArray() (or JsonNode.isObject()) to perform this check.
Then you can parse the node into a list with ObjectReader.readValue() or into a POJO using ObjectMapper.treeToValue().
String myJson = """
{"simple" : {"name":"c", "email" : "c#z.com"} }
""";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(myJson);
if (node.isArray()) {
ObjectReader reader = mapper.readerFor(new TypeReference<List<Simple>>() {});
List<Simple> list = reader.readValue(node);
// do something with a list
} else {
Simple pojo = mapper.treeToValue(node, Simple.class);
// do something else with a single object
}
Jackson is able to parse any json into a map where value is any object. you can then inquire on the type of the map value
Map<String, Object> map = new ObjectMapper().readValue(jsonInput, Map.class);
Object value = map.get("simple");
if (value instanceof Collection) { // will return false for null
Collection<Simple> simples = (Collection<Simple>)value;
}
else if (value instanceof Simple) {
Simple simple = (Simple)value;
}
else {
System.err.println("unrecognized");
}
You only need to read the first node, simple and check if it is an array - using isArray() method.
public class Main{
public static void main(String args[]) {
//String inputString = [your input];
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(inputString);
JsonNode simpleNode = root.findPath("simple");
if(simpleNode.isArray()) {
//you have an array
} else {
// you have an element
}
}
}
I am trying to parse the json array with same key value which looks something like:
Back End Response:"Countries":[{"state":"Queens Land "state":"Tasmania"}].
2.I have created classes to read back end response and mapping the values with faster XML, but only the last value in the array is getting copied, instead of entire array. This is how I created my Data Transfer Object classes.
Now the Test object contains Countries array, but only one of the State value is read. i.e
"Countries":["States":"Tasmania"].
Please excuse me for typos. can some one help, can some one suggest whats wrong with the bellow code..
private Class Test{
List<Countries> countries;
}
private class Countries{
private String States;
}
private class Mapper {
}
In my Mapper class reading the value using faster XML
Assume that your JSON payload is:
{
"Countries": [
{
"state": "Queens Land",
"state": "Tasmania"
}
]
}
According to RFC7159:
An object structure is represented as a pair of curly brackets
surrounding zero or more name/value pairs (or members). A name is a
string. A single colon comes after each name, separating the name
from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique.
In your example, you have not unique names and most JSON parsers would skip repeated values and would take only one. So, if you can change backend response, just change it to:
{
"Countries": [
{
"state": "Queens Land"
},
{
"state": "Tasmania"
}
]
}
or
{
"Countries": [
"Queens Land",
"Tasmania"
]
}
But if you can not do that, you need to use Streaming API and implement your custom deserialiser. See below example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonPathApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Test test = mapper.readValue(jsonFile, Test.class);
System.out.println(test);
}
}
class CountriesJsonDeserializer extends JsonDeserializer<Countries> {
#Override
public Countries deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
List<String> states = new ArrayList<>();
while (p.nextToken() != JsonToken.END_OBJECT) {
if (p.currentToken() == JsonToken.FIELD_NAME) {
if ("state".equalsIgnoreCase(p.getText())) {
p.nextToken();
states.add(p.getText());
}
}
}
Countries countries = new Countries();
countries.setStates(states);
return countries;
}
}
class Test {
#JsonProperty("Countries")
private List<Countries> countries;
public List<Countries> getCountries() {
return countries;
}
public void setCountries(List<Countries> countries) {
this.countries = countries;
}
#Override
public String toString() {
return "Test{" +
"countries=" + countries +
'}';
}
}
#JsonDeserialize(using = CountriesJsonDeserializer.class)
class Countries {
private List<String> states;
public List<String> getStates() {
return states;
}
public void setStates(List<String> states) {
this.states = states;
}
#Override
public String toString() {
return "Countries{" +
"states=" + states +
'}';
}
}
Above example prints:
Test{countries=[Countries{states=[Queens Land, Tasmania]}]}
See also:
Intro to the Jackson ObjectMapper
First of all, i am new to this so please pardon me. Have been working on a music app and I am trying to parse JSON code from a streaming link and display "artist" name and "title" of song to my app users. But i am having issues collecting the data.
Here is my JSON code from the streaming link:
{"type":"result","data":[{"title":"My Stream ","song":"Unknown - The Authorised One","track":{"artist":"Unknown Artist","title":"The Authorised One","album":"Unknown","royaltytrackid":181938.0000,"started":1498151105,"id":181938,"length":0,"playlist":{"id":3520,"title":"Rev Arome E. Adah"},"buyurl":"https:\/\/itunes.apple.com\/us\/album\/the-unknown-god\/id772022436?uo=4","imageurl":"http:\/\/is5.mzstatic.com\/image\/thumb\/Music5\/v4\/d7\/6d\/52\/d76d52df-db43-7130-0e37-62241ff50a21\/source\/100x100bb.jpg"},"bitrate":"128 Kbps","server":"Online","autodj":"Online","source":"Yes","offline":false,"summary":"<a href=\"http:\/\/cp9.serverse.com:2199\/tunein\/-stream\/svhxmwhp.pls\">Eloti Designs Stream - Unknown - The Authorised One<\/a>","listeners":0,"maxlisteners":1000,"reseller":0,"serverstate":true,"sourcestate":true,"sourceconn":1,"date":"Jun 22, 2017","time":"07:06 PM","rawmeta":"Unknown - The Authorised One ","mountpoint":"\/stream","tuneinurl":"http:\/\/209.133.216.3:7550\/stream","directtuneinurl":"","proxytuneinurl":"http:\/\/209.133.216.3\/proxy\/svhxmwhp?mp=\/stream","tuneinformat":"mp3","webplayer":"muses","servertype":"ShoutCast2","listenertotal":0,"url":"http:\/\/cp9.serverse.com:2199\/rpc"}]
I used this code to post "artist" name of "Unkwown Artist" to my text field but it didn't work for me.
JSONObject parentObject = new JSONObject(finalJson);
JSONArray parentArray = parentObject.getJSONArray("data");
JSONObject finalObject = parentArray.getJSONObject(0);
String songName = finalObject.getString("artist");
return songName;
track is a jsonobject containing artist and track is inside first jsonobject of
data array so fetch track then fetch artist from it
String songName = finalObject.getJSONObject("track").getString("artist");
{
"type":"result",
"data":[ // fetch JSONArray
{ // fetch first JSONObject
"title":"My Stream ","song":"Unknown - The Authorised One",
"track":{ // fetch track JSONObject
"artist":"Unknown .." // fetch string
Have you tried the jackson parser? It's super easy to use and can easily parse the string above. All you need to do is create 3/4 POJO classes that map your structure and then apply the readValue function of the Mapper to the outer class. Following is a small example with a List and one inner class:
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
class OutterPojo {
public String aString;
public List<InnerPojo> aList;
public String getaString() {
return aString;
}
public void setaString(String aString) {
this.aString = aString;
}
public List<InnerPojo> getaList() {
return aList;
}
public void setaList(List<InnerPojo> aList) {
this.aList = aList;
}
#Override
public String toString() {
return new StringBuilder().append("{ #OutterPojo# ").append("aString:").append(aString).append(", ").append("aList:")
.append(aList).append(" }").toString();
}
}
class InnerPojo {
public String anotherString;
public String getanotherString() {
return anotherString;
}
public void setanotherString(String anotherString) {
this.anotherString = anotherString;
}
#Override
public String toString() {
return new StringBuilder().append("{ #InnerPojo# ").append("anotherString:").append(anotherString).append(" }")
.toString();
}
}
public class Test {
public static void main(String args[]) throws JsonParseException, JsonMappingException, IOException {
String jsonData = "{\"aString\":\"s\",\"aList\":[{\"anotherString\":\"ss\"},{\"anotherString\":\"sss\"}]}";
ObjectMapper objectMapper = new ObjectMapper();
OutterPojo testObject = objectMapper.readValue(jsonData, OutterPojo.class);
System.out.println(testObject);
}
}
As to dependencies all you need for this to work is jackson-core, jackson-databind and jackson-annotations - here's the link to maven:
https://mvnrepository.com/artifact/com.fasterxml.jackson.core
Hope it helps! :)
For example my JSON text is coming like this.
"pages":{"42010":{"pageid":42010,"ns":0,"title":"Queen (band)"}}
Because everytime my json text is coming with different number which is inside pages tag.
How do i convert this to Java equivalent class?
Currently my generated java class is something like this.
#Generated("org.jsonschema2pojo")
public class Pages {
#SerializedName("42010")
#Expose
private _42010 _42010;
}
That _42010 class contains the inner fields like "pageid":42010,"ns":0,"title":"Queen (band)", since i am getting everytime new number inside pages, its not working. its working only for the specific json text.
You can use a custom deserialiser that ignored the changing number. For example:
package jacksonTest;
import java.io.IOException;
import java.lang.reflect.Type;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class CustomDeserialiser {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
String json = "{\"42010\":{\"pageid\":42010,\"ns\":0,\"title\":\"Queen (band)\"}}";
String json2 = "{\"12345\":{\"pageid\":12345,\"ns\":0,\"title\":\"Queen (band)\"}}";
Gson g = new GsonBuilder().registerTypeAdapter(Pages.class, new PagesDeserialiser()).create();
Pages fromJson = g.fromJson(json, Pages.class);
System.out.println(fromJson);
fromJson = g.fromJson(json2, Pages.class);
System.out.println(fromJson);
}
public static class PagesDeserialiser implements JsonDeserializer<Pages> {
#Override
public Pages deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws com.google.gson.JsonParseException {
JsonObject object = json.getAsJsonObject();
Pages p = new Pages();
object.entrySet().forEach( e -> {
JsonObject tmp = e.getValue().getAsJsonObject();
if(tmp.get("pageid") != null) {
// right object
p._42010 = new _42010();
p._42010.ns = tmp.get("ns").getAsInt();
p._42010.pageid = tmp.get("pageid").getAsInt();
p._42010.title = tmp.get("title").getAsString();
}
});
return p;
}
}
public static class Pages {
_42010 _42010;
#Override
public String toString() {
return _42010.toString();
}
}
public static class _42010 {
int pageid;
int ns;
String title;
#Override
public String toString() {
return title + " " + pageid + " " + ns;
}
}
}
The deserialiser for type pages simply checks the entries to find the one that contains a pageId and then populates the class.
Running my test gives you:
Queen (band) 42010 0
Queen (band) 12345 0
I am assuming that you are using Gson as your json library.
Regards,
Artur
Why do not use an JSON library like jackson or org.json?
Make your json correct like
{
"pages":{
"42010":{
"pageid":42010,
"ns":0,
"title":"Queen (band)"
}
}
}
And you will be able to use it like :
JSONObject jsonObjet = new JSONObject(yourJson);
jsonObjet.get("pages");
Ideally it should be using Map.
This helps in forming the values as Map<Integer, Pojo>.
Lets say
public class Pojo{
private int pageid;
private String title;
private int ns;
// getter and setter
}
This suffices the requirement of holding the random digits, generated at runtime,
I'm trying to read a json file into java using Gson. So for that I used this line:
data = gson.fromJson(json.toString(), MainData.class);
I assumed It worked as I can access a field named version inside MainData:
System.out.println(data.version);//<-- this works
But as soon as I try to retrieve data from the inner objects I get a nullPointerException:
System.out.println(data.elemental.Water.a);//<-- this doens't work
I looked at a lot of questions that looked like they were the same. But from what I could tell I was doing excactly the same. So my guess is that there is a syntax error somewhere but I just cannot find it. Wich is not that unthinkable as this is my first try to work with json and gson.
Here is my json file:
{
"version": "1.1.0",
"values": {
"game": {
},
"elemental": {
"Air": {
},
"Void": {
},
"Earth": {
},
"Water": {
"a": "10"
},
"Fire": {
"tickTime": "10"
}
}
}
}
And here my java file:
By the way I also noticed that not all of the constructors are called.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import org.bukkit.craftbukkit.libs.com.google.gson.Gson;
public class JSONReader {
private static MainData data;
protected static final void readFile() throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(Elementals.DataBase, "advanced.json"))));
StringBuffer json = new StringBuffer();
String line;
while((line = reader.readLine()) != null){
json.append(line);
}
System.out.println(json.toString());
reader.close();
Gson gson = new Gson();
data = gson.fromJson(json.toString(), MainData.class);
System.out.println(data.version);//works
System.out.println(data.elemental.Water.a);//doesn't work
System.out.println(data.elemental.Fire.tickTime);//doesn't work
}
private class MainData{
MainData(){//is called
System.out.println("main");
}
private String version;
private GameData values;
private ElementalData elemental;
public class GameData{
GameData(){//is not called
System.out.println("gamedata");
}
}
private class ElementalData{
ElementalData(){//is not called
System.out.println("elemental");
}
private AirData Air;
private EarthData Earth;
private WaterData Water;;
private FireData Fire;
private VoidData Void;
private class AirData{
}
private class EarthData{
}
private class WaterData{
WaterData(){//is not called
System.out.println("Water");
}
private int a;
}
private class FireData{
private int tickTime;
}
private class VoidData{
}
}
}
}
I hope this was informative enough and I'm looking forward to an answer.
In your JSON, your elemental is nested directly within values, not within the root object. Your POJOs need to reflect that. Your class should be like this
public class GameData {
GameData() {// is not called
System.out.println("gamedata");
}
private ElementalData elemental; // instead of in MainData
}
and then
System.out.println(data.values.elemental.Water.a);