1

I'm trying to parse the below Json using the Gson lib in Java. When using other languages, such as C#, this JSON is parsed into an array, however it seems Gson converts this into a set of java attributes (which to be honest, makes more sense to me). Does anyone know if I can change this behaviour of the Gson lib?

{
  "Outer": {
    "0": {
      "Attr1": 12345,
      "Attr2": 67890
    },
    "1": {
      "Attr1": 54321,
      "Attr2": 09876
    }
  }
}

The below code demonstrates how Gson parses the array as a JsonObject. To be clear, I realise I've referenced outer as a JsonObject but I was just doing this to demonstrate the code. If I try and reference outer as an JsonArray, the code fails.

String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder()
            .disableHtmlEscaping()
            .setLenient()
            .serializeNulls()
            .create();

JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");

System.out.println(outer);
System.out.println(outer.isJsonArray());

Result:

{"0":{"Attr1":12345,"Attr2":67890},"1":{"Attr1":54321,"Attr2":"09876"}}
false

//edit I'm using this current simple Json as an example, however my application of this code will be to parse Json that's of varying and unknown shape. I therefore need Gson to automatically parse this to an array so that the isJsonArray returns true.

5
  • Actually, your JSON is not an array, it is an array inside an object. What do isJsonArray() return with String json = "\"[Outer\": { \ [...] }] ? Commented Apr 3, 2020 at 10:36
  • That would be an array. I am told that the above example would be parsed to an array by JavaScript and a c# lib and I'm trying to replicate that behaviour in java with Gson. Commented Apr 3, 2020 at 10:49
  • Maybe I'm not understanding you but as fas as I understand Js doesn't parse your example json into an arry but into an object with two parameters: imgur.com/vxL043G Commented Apr 3, 2020 at 11:20
  • I agree @CarlosLópezMarí, but apparently JavaScript and c# parses this as an array, which I was trying to mimic in Java. Commented Apr 3, 2020 at 11:43
  • The image I peasted is from Javascript. Commented Apr 3, 2020 at 15:03

4 Answers 4

2

TL;DR: See "Using Deserializer" section at the bottom for parsing straight to array.


That JSON does not contain any arrays. An array would use the [...] JSON syntax.

Normally, a JSON object would map to a POJO, with the name in the name/value pairs mapping to a field of the POJO.

However, a JSON object can also be mapped to a Map, which is especially useful when the names are dynamic, since POJO fields are static.

Using Map

The JSON object with numeric values as names can be mapped to a Map<Integer, ?>, e.g. to parse that JSON to POJOs, do it like this:

class Root {
    @SerializedName("Outer")
    public Map<Integer, Outer> outer;
    @Override
    public String toString() {
        return "Root[outer=" + this.outer + "]";
    }
}
class Outer {
    @SerializedName("Attr1")
    public int attr1;
    @SerializedName("Attr2")
    public int attr2;
    @Override
    public String toString() {
        return "Outer[attr1=" + this.attr1 + ", attr2=" + this.attr2 + "]";
    }
}

Test

Gson gson = new GsonBuilder().create();
Root root;
try (BufferedReader in = Files.newBufferedReader(Paths.get("test.json"))) {
    root = gson.fromJson(in, Root.class);
}
System.out.println(root);

Output

Root[outer={0=Outer[attr1=12345, attr2=67890], 1=Outer[attr1=54321, attr2=9876]}]

Get as Array

You can then add a helper method to the Root class to get that as an array:

public Outer[] getOuterAsArray() {
    if (this.outer == null)
        return null;
    if (this.outer.isEmpty())
        return new Outer[0];
    int maxKey = this.outer.keySet().stream().mapToInt(Integer::intValue).max().getAsInt();
    Outer[] arr = new Outer[maxKey + 1];
    this.outer.forEach((k, v) -> arr[k] = v);
    return arr;
}

Test

System.out.println(Arrays.toString(root.getOuterAsArray()));

Output

[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]

Using Deserializer

However, it would likely be more useful if the conversion to array is done while parsing, so you need to write a JsonDeserializer and tell Gson about it using @JsonAdapter:

class Root {
    @SerializedName("Outer")
    @JsonAdapter(OuterArrayDeserializer.class)
    public Outer[] outer;

    @Override
    public String toString() {
        return "Root[outer=" + Arrays.toString(this.outer) + "]";
    }
}
class OuterArrayDeserializer implements JsonDeserializer<Outer[]> {
    @Override
    public Outer[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        // Parse JSON array normally
        if (json.isJsonArray())
            return context.deserialize(json, Outer[].class);

        // Parse JSON object using names as array indexes
        JsonObject obj = json.getAsJsonObject();
        if (obj.size() == 0)
            return new Outer[0];
        int maxKey = obj.keySet().stream().mapToInt(Integer::parseInt).max().getAsInt();
        Outer[] arr = new Outer[maxKey + 1];
        for (Entry<String, JsonElement> e : obj.entrySet())
            arr[Integer.parseInt(e.getKey())] = context.deserialize(e.getValue(), Outer.class);
        return arr;
    }
}

Same Outer class and test code as above.

Output

Root[outer=[Outer[attr1=12345, attr2=67890], Outer[attr1=54321, attr2=9876]]]
Sign up to request clarification or add additional context in comments.

Comments

0

I'll asume your JsonObject is a POJO class such like:

public Inner[] outer;

If you want an array of objects you can change your code to:

Inner[] jo = gson.fromJson(json, Inner[].class);

1 Comment

Thanks Carlos, I've updated my post to try and explain further.
0
Jackson – Marshall String to JsonNode will be useful in your case.with following pom:- 


    //POM FILE


        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>

//JAVA CODE
    //read json file data to String


     byte[] jsonData = Files.readAllBytes(Paths.get("employee.txt"));

        //create ObjectMapper instance
        ObjectMapper objectMapper = new ObjectMapper();

        //read JSON like DOM Parser
        JsonNode rootNode = objectMapper.readTree(jsonData);
        JsonNode idNode = rootNode.path("id");
        System.out.println("id = "+idNode.asInt());

        JsonNode phoneNosNode = rootNode.path("phoneNumbers");
        Iterator<JsonNode> elements = phoneNosNode.elements();
        while(elements.hasNext()){
            JsonNode phone = elements.next();
            System.out.println("Phone No = "+phone.asLong());
        }

You can use the JsonNode class's method findParent findValue and findPath which reduce your code as compare to another parsing library.

Comments

0

Please refer below code 1.To get an array of Objects (outerArray) 2.You can extract a JsonArray (outerJsonArray) containing values of inner objects in Outer (in case keys aren't significant for further use)

String json = "{\"Outer\": { \"0\": { \"Attr1\": 12345, \"Attr2\": 67890 }, \"1\": { \"Attr1\": 54321, \"Attr2\": 09876 }}}";
Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().serializeNulls().create();
JsonObject jo = gson.fromJson(json, JsonObject.class);
JsonObject outer = jo.getAsJsonObject("Outer");

Object[] outerArray = outer.entrySet().toArray();
// outerArray: [0={"Attr1":12345,"Attr2":67890}, 1={"Attr1":54321,"Attr2":"09876"}]

JsonArray outerJsonArray = new JsonArray();
outer.keySet().stream().forEach(key -> {
outerJsonArray.add(outer.get(key));
});
//jsonArray=[{"Attr1":12345,"Attr2":67890},{"Attr1":54321,"Attr2":"09876"}]

System.out.println(outer);
System.out.println(outerJsonArray.isJsonArray() + " " + outerJsonArray);

2 Comments

Thanks tir0w. Unfortunately, I don't know the shape of the JSON that I'll be parsing so I can't write code specifically for a case. I need a way to understand if the above 'array' scenario is happening in an unknown shaped JSON file. eg. the attribute "outer" may not exist, or could be at another level. I currently rely on isJsonArray to determine if it's an array and its the behaviour of this function that I'm trying to change.
@JAC2703, well, it's not an array, that's why you are not getting an array. I think your approach is correct but yoir example isn't.

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.