Techno

things which came out my laptop

Expected BEGIN_ARRAY but Was BEGIN_OBJECT

| Comments

I had a problem with Google GSON deserialization. JSON data which I was trying to parse using GSON library looks like this {"matches":{"matchId":"bla" "score" : "bla"}} and when there is more roles available it looks like this {"matches":[{"matchId":"bla" "score" : "bla"}{"matchId":"hihi" "score" : "hihi"}]}. So sometimes “matches” data comes as “Object” or “ARRAY” format depending upon number of matches available.

GSON fails as expected to parse this in to Java class which has field of type List class, When JSON data comes with matches as “Object” format, it throws exception “Expected BEGIN_ARRAY but was BEGIN_OBJECT”.

Source JSON data it self is bogus here, correct fix should be at JSON generator which should always return ARRAY format for array objects irrespective of number of objects being sent.

Unfortunately I don’t have access to JSON response generator, so I decided to write own GSON’s TypeAdapter implementation for List type which handles this single instance case, deserializes it and then transform into an List and set it on the field, making it uniform.

Following is implementation of ArrayAdapterFactory,

(ArrayAdapter.java) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.blog.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;


public class ArrayAdapter<T> extends TypeAdapter<List<T>> {
  private Class<T> adapterclass;

  public ArrayAdapter(Class<T> adapterclass) {
      this.adapterclass = adapterclass;
  }

  public List<T> read(JsonReader reader) throws IOException {

      List<T> list = new ArrayList<T>();

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

      if (reader.peek() == JsonToken.BEGIN_OBJECT) {
          T inning = gson.fromJson(reader, adapterclass);
          list.add(inning);

      } else if (reader.peek() == JsonToken.BEGIN_ARRAY) {

          reader.beginArray();
          while (reader.hasNext()) {
              T inning = gson.fromJson(reader, adapterclass);
              list.add(inning);
          }
          reader.endArray();

      }

      return list;
  }

  public void write(JsonWriter writer, List<T> value) throws IOException {

  }

}
(ArrayAdapterFactory.java) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.blog.util;;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;

public class ArrayAdapterFactory implements TypeAdapterFactory {

  @SuppressWarnings({ "unchecked", "rawtypes" })
  @Override
  public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {

      TypeAdapter<T> typeAdapter = null;

      try {
          if (type.getRawType() == List.class)
              typeAdapter = new ArrayAdapter(
                      (Class) ((ParameterizedType) type.getType())
                              .getActualTypeArguments()[0]);
      } catch (Exception e) {
          e.printStackTrace();
      }

      return typeAdapter;
      

  }

}

While constructing GSON instance, I assigned TypeAdapterFactory with ArrayAdapterFactory to GsonBuilder,

1
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ArrayAdapterFactory()).create();

Comments