4

I have a simple class containing name variable of type java.lang.CharSequence

    class Person {
    public java.lang.CharSequence name;
}

When I try to deserialize a JSON String using GSON library

        Person p;
        Gson gson = new Gson();
        String json = "{\"name\":\"dinesh\"}";
        p = gson.fromJson(json, Person.class);
        System.out.println(p);

It gives me the following error:

java.lang.RuntimeException: Unable to invoke no-args constructor for interface java.lang.CharSequence. Registering an InstanceCreator with Gson for this type may fix this problem.

How do I fix this? I cannot change the Person.name type to String.

As suggested in comments,

I created a custom adapter for CharSequence

class CharSequenceAdapter extends TypeAdapter<CharSequence> {

    @Override
    public void write(JsonWriter out, CharSequence value) throws IOException {
        
    }

    @Override
    public CharSequence read(JsonReader in) throws IOException {
        String s = new String();
        in.beginObject();
        while(in.hasNext()) {
            s = in.nextString();
        }
        return s;
    }
    
}

And my GSON builder looks like this:

Person p;
GsonBuilder builder = new GsonBuilder().registerTypeAdapter(java.lang.CharSequence.class, new CharSequenceAdapter());
Gson gson = builder.create();
String json = "{\"name\":\"dinesh\"}";
p = gson.fromJson(json, Person.class);
System.out.println(p);

Now it gives me another error:

Expected BEGIN_OBJECT but was STRING at line 1 column 10 path $.name

What did I miss?

I don't think it's a duplicate. All the other questions talk about deserializing one class or interface as a whole. I am having a problem with a class that has interface references as member variables. I couldn't solve the problem from similar answers.

Kirby
  • 15,127
  • 10
  • 89
  • 104
Dinesh Raj
  • 193
  • 1
  • 2
  • 11

3 Answers3

2

CharSequence is an interface.

When Gson tries to deserialize a json string into an object it “introspects” the object (by using reflection) and tries to resolve the types of fields. Then it tries to create a field of that time.

Out of the box Gson can deal with many “widespread” types like String, Integer, Boolean and so forth, however when its something GSon is not aware of (Like CharSequence in this case), GSon stops with Error.

Now its clear that you should “teach” Gson to understand this custom type. For this purpose there are type adapters in Gson.

Here you can find a short tutorial on using the adapters. I won’t re-write here an example from there, but in general you should create an adapter, register it on Gson object and call your code. When Gson reaches the CharSequence field it will find this custom adapter and invoke it

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
2

As mentioned in the other answers, Gson has no built-in adapter for CharSequence (see related pull request) and is therefore unable to deserialize it.

However, you can solve this by writing a custom TypeAdapter, such as the following:

class CharSequenceTypeAdapter extends TypeAdapter<CharSequence> {
    @Override
    public void write(JsonWriter out, CharSequence value) throws IOException {
        if (value == null) {
            out.nullValue();
        } else {
            // Assumes that value complies with CharSequence.toString() contract
            out.value(value.toString());
        }
    }

    @Override
    public CharSequence read(JsonReader in) throws IOException {
        if (in.peek() == JsonToken.NULL) {
            // Skip the JSON null
            in.skipValue();
            return null;
        } else {
            return in.nextString();
        }
    }
}

This assumes that your CharSequence value is encoded as JSON string value (e.g. "value") and not as JSON object ({ ... }). For serialization it also assumes that the value you are using complies with the CharSequence.toString() contract.

You have to register the adapter then with a GsonBuilder which you use to create the Gson instance:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(CharSequence.class, new CharSequenceTypeAdapter())
    .create();

Alternatively, if you actually always use String as CharSequence value then you could also change the field types to String, in case that does not have any negative effect on the API of your code.

Marcono1234
  • 5,856
  • 1
  • 25
  • 43
  • Thanks Marcono, it's working, please add same answer here as well, https://stackoverflow.com/q/72591268/5359340 question is different, better if you add same answer there as well – FGH Aug 15 '22 at 06:52
0

You don't need the in.beginObject(); line. Without that line, the code works fine.

Kirby
  • 15,127
  • 10
  • 89
  • 104