Home > Back-end >  Deserializing Java Generics with Gson
Deserializing Java Generics with Gson

Time:07-12

I'm writing a library for my WebSocket (STOMP) on my webserver. I'm trying to make it so that the library will return the object specified in the Generics.

Add stomp client

public <T> void addStompClient(T t, String url, String subscription, StompMessageListener<T> listener) {
    StompClient<T> sc = new StompClient<T>(t, URI.create(url));
    StompConnectionListener scl = new StompConnectionListener() {
        public void onConnecting() {}
        public void onConnected() {
            sc.subscribe(subscription, listener);
        }
        public void onDisconnected() {}
        
    };
    sc.setStompConnectionListener(scl);
    sc.connect();
    clients.add(sc);
}

StompMessageListener

@FunctionalInterface
public interface StompMessageListener<T> {

    void onMessage(T test);

}

StompClient class method and variables

@Getter
private T t;

protected StompClient(T tclass, URI serverUri) {
    super(serverUri);
    t = tclass;
}

@SuppressWarnings("unchecked")
@Override
public void onMessage(String message) {
    StompFrame stompFrame = StompFrame.fromString(message);
    
    switch (stompFrame.getCommand()) {
    case CONNECTED:
        stompConnected = true;
        if (stompConnectionListener != null) {
            stompConnectionListener.onConnected();
        }
        break;
    case MESSAGE:
        Integer subscriptionId = Integer.valueOf(stompFrame.getHeaders().get(StompHeader.SUBSCRIPTION.toString()));
        
        Class<T> tclass = (Class<T>) t.getClass();
        T test = (T) new Gson().fromJson(stompFrame.getBody(), tclass);
        
        StompSubscription<T> listener = (StompSubscription<T>) subscriptions.get(subscriptionId);
        listener.getListener().onMessage(test);
        break;
    case DISCONNECT:
        stompConnected = false;
        if (stompConnectionListener != null) {
            stompConnectionListener.onDisconnected();
        }
        break;
    default:
        break;
    }
}

Calling:

clientManager.addStompClient(Category.class, "ws://localhost:8080/stomp-api/v1/hub", "/stomp-api/v1/listener/category/create", (category) -> {
        Bukkit.getConsoleSender().sendMessage(category.getName());
    });

But in doing this, I get a:

java.lang.UnsupportedOperationException: Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?

Anyone know how to fix this? I tried to add the type adapter but couldn't figure out how to do it properly

CodePudding user response:

If anyone runs across this, here is how I solved this using the comment from

Alternately, you can use Jackson, which can deserialize from a Class. Here's an example. – Christopher Schneider

@Override
public void onMessage(String message) {
    StompFrame stompFrame = StompFrame.fromString(message);
    
    switch (stompFrame.getCommand()) {
    case CONNECTED:
        stompConnected = true;
        if (stompConnectionListener != null) {
            stompConnectionListener.onConnected();
        }
        break;
    case MESSAGE:
        Integer subscriptionId = Integer.valueOf(stompFrame.getHeaders().get(StompHeader.SUBSCRIPTION.toString()));
        
        ObjectMapper objectMapper = new ObjectMapper(new Gson());
        T t = objectMapper.readValue(stompFrame.getBody(), tclass);
        
        StompSubscription<T> listener = (StompSubscription<T>) subscriptions.get(subscriptionId);
        listener.getListener().onMessage(t);
        break;
    case DISCONNECT:
        stompConnected = false;
        if (stompConnectionListener != null) {
            stompConnectionListener.onDisconnected();
        }
        break;
    default:
        break;
    }
}

The ObjectMapper was put in place. tclass = Class. This allowed me to use Jackson and Generics to provide the Object that will be returning from the stomp listener

  • Related