37

GSON appears to be doing some kind of trick where it looks at the internal fields of my JavaBeans instead of using the publically-accessible property information. Unfortunately this won't fly for us because our magically-created beans are full of private fields which I don't want it to store off.

@Test
public void testJson() throws Exception
{
    Player player = new MagicPlayer(); //BeanUtils.createDefault(Player.class);
    player.setName("Alice");

    Gson gson = new GsonBuilder()
        .registerTypeAdapter(Player.class, new PlayerTypeAdapter())
        .create();

    System.out.println(gson.toJson(bean));
}

private static class PlayerTypeAdapter implements JsonSerializer<Player>
{
    @Override
    public JsonElement serialize(Player player, Type type,
                                 JsonSerializationContext context)
    {
        throw new RuntimeException("I got called, woohoo");
    }
}

public static interface Player //extends SupportsPropertyChanges
{
    public String getName();
    public void setName(String name);
}

// Simple implementation simulating what we're doing.
public static class MagicPlayer implements Player
{
    private final String privateStuff = "secret";
    private String name;

    @Override
    public String getName()
    {
        return name;
    }

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

This gives:

{"privateStuff":"secret","name":"Alice"}

And of course, never calls my type adapter, which seemingly makes it impossible to get any other behaviour.

Hakanai
  • 12,010
  • 10
  • 62
  • 132
  • Could you add the code that calls Gson.toJson() ? – Jesse Wilson May 18 '12 at 05:31
  • It's not clear this is the same problem, but I ran into something similar a while ago, perhaps it will help you: http://stackoverflow.com/questions/5952595/serializing-list-of-interfaces-gson/15557654#15557654 – dimo414 Apr 20 '14 at 05:09

3 Answers3

72

I had the same issue, with version 2.3.1 of Gson. I got it working by using

.registerTypeHierarchyAdapter(Player.class, new PlayerTypeAdapter())

instead of

.registerTypeAdapter(Player.class, new PlayerTypeAdapter())
nont
  • 9,322
  • 7
  • 62
  • 82
9

Current release of GSON does not work this way. This will serialize your type using the adapter.

gson.toJson(bean, Player.class)

Alternatively you can register the PlayerTypeAdapter for MagicPlayer.class and your existing code will work. GSON looks up the TypeAdapter by the concrete (or top level) type only. So you will have to register a custom type adapter for every concrete type you may encounter.

There is TypeHeirarchyAdapter for when you want the same TypeAdapter to be used for all extending classes of a given type. I have not had good luck with this though and have not really spent time looking into why it has not worked well for me. Here is some relevant discussion: https://groups.google.com/forum/?fromgroups=#!searchin/google-gson/interface/google-gson/0ebOxifqwb0/OXSCNTiLpBQJ

Eric Schoonover
  • 47,184
  • 49
  • 157
  • 202
  • This answers the main part of the question about why it doesn't work. It doesn't exactly solve the problem or make GSON usable in any way, because relying on the code to tell it the type of everything is never going to work. e.g., there is another place where I have a List where each object could be any type. Now what am I supposed to do? – Hakanai Aug 24 '12 at 04:32
  • The second part of the question explains what to do. For each concrete type you need to register the `TypeAdapter` you want to use. – Eric Schoonover Aug 26 '12 at 17:58
-3

Why it does not work:

  1. PlayerTypeAdapter should implements JsonSerializer. Does it?

  2. If it does - than should work. It work perfect for me, so don't see reason why it does not work for you. Start you application in debug, put breakpoints and checkout. There should be some reason.

In your test where is Gson builder with adapter? You should change it:

public void testJson() throws Exception
{
    Player player = new MagicPlayer(); //BeanUtils.createDefault(Player.class);
    player.setName("Alice");

    // this is wrong if you want adapter
    /* Gson gson = new Gson(); */

    // this is what you nead
     Gson gson = new GsonBuilder()
        .registerTypeAdapter(Player.class, new PlayerTypeAdapter())
        .create();

    System.out.println(gson.toJson(bean));
}
alexey28
  • 5,170
  • 1
  • 20
  • 25
  • You can see in the question above how PlayerTypeAdapter is implemented. I put a breakpoint on the line where I am throwing the exception, but the line is never reached. Ergo, Gson is not calling my serialiser. There must be something else I'm supposed to do. Either that, or Gson doesn't call type adapters for interfaces (which just means I can't use it.) – Hakanai May 17 '12 at 22:27
  • Okay, updated to remove CGLIB from the equation. Gson still doesn't call my type adapter at any point. – Hakanai May 17 '12 at 22:35
  • 5
    Please read the question before updating your answer any further. – Hakanai May 20 '12 at 23:53