5

I have a generic response wrapper class:

public class Response <T> {
    T response;
}

and unrelated classes to be wrapped:

public class ServiceResponse {
    String someField;
}

When I make a service request, I get a JSON response that looks something like:

{ "code":200, "response":{"someField":"some text"} }

Now, all my service responses have the same outer wrapper, i.e., they all have:

{ "code":200, "timestamp":"....", "response":... }

But the actual format/type of the response field is different for each service request. When I deserialize the response, I need to know the type of the response field so I can create the appropriate instance, if the deserialization was done within Response, I could use:

response = new T(jsonParser);

However, I'm doing all of this from within a library that is driven by reflection, so I normally deserialize the whole tree with code like:

wrapper = deserializer.parseObject(Response<ServiceResponse>.class)

but, at this point my parseObject method can't correctly determine the type of T.

I can use something like:

Response<ServiceResponse> response = new Response<>();
Field field = response.getClass().getDeclaredField("response");
Type type = field.getGenericType();

which then tells me that response is of type T but what I actually need is ServiceResponse

Per this SO question I tried casting as ParameterizedType but that would actually seem to apply to a field of type Response<ServiceResponse> and not the actual field within (and it fails because type can't be cast as ParameterizedType)

Is there any way to determine (at run time) the raw type of response?

Eventually, I may wind up having to create an annotation providing more details about how to deserialize the field, probably by providing a function to do it, but would prefer a more transparent approach.

Another possibility might be to actually assign a void instance of T to response at initialization time and then I could grab the actual type from that...

Community
  • 1
  • 1
David Berry
  • 40,941
  • 12
  • 84
  • 95
  • Short answer: no. Long answer: provide more details about how you use `Response`. The small snippet where you try to get the `Field` is misleading because there's no point doing that, you already know you have a `ServiceResponse`. How do you get your `Response>` to begin with? – Savior Apr 08 '16 at 16:41
  • Provided more information about the use case, but essentially I'm trying to deserialize the `Response>` from a network service request. At the outer level I know what ? is, but within the deserialization code I don't, so I'm trying to recover it from the reflection information available. – David Berry Apr 08 '16 at 16:56
  • And yeah, I'm afraid that your short answer is probably the actual answer since generics are really a convenient compiler fiction. – David Berry Apr 08 '16 at 16:57
  • I'm a little confused then. If, at the outer level you know what `?` is, then just provide that to the deserialization code. If, at compile time, you know you want a `Response`, then just pass it the `ServiceResponse.class`, you don't need to (you can't, really) retrieve that from the `Response`. In the deserialization code, the alternative is to have an indication in the JSON of the deserialization strategy. Jackson some times uses something like a `@class` field with the fully qualified target class name as its value. – Savior Apr 08 '16 at 16:59
  • Passing `ServiceResponse` to the deserialization would require specialization for this specific case. I'm looking for a generic solution that would allow me to use: `deserialize(Foo.class)` that would correctly instantiate any fields with the appropriate types in a `Foo` object. – David Berry Apr 08 '16 at 17:06
  • Can we go to chat (I don't have enough reputation to start one)? Obviously Java can't guess what type you need from the JSON. You either have to tell it or the JSON has to. There's no `.class` syntax for parameterized generic classes. Most JSON serializers will have a notion of type token. So, instead of `Foo.class`, you'd pass a `Type` object (a `ParameterizedType` instance) that represents that particular parameterization. – Savior Apr 08 '16 at 17:11
  • If you step outside the language, you can get your hands on the type information of the generic. Are you interested in that, or do you insistent on doing from Java at runtime? – Ira Baxter Apr 08 '16 at 17:25
  • @IraBaxter that's another option, but for this I'd just as soon not bother with it. As much as anything else I put this stuff together as a learning exercise. I have a solution that works, just exploring other ways to fix it. – David Berry Apr 08 '16 at 17:28
  • @pillar I have a solution already that allows me to `deserialize(Foo.class)` and then by iterating the fields of Foo read and/or write approproiate information. In fact, it looks like I can even apply similar logic to generic typed fields within the object. The problem arises when my outer most class is generic and has a parameterized field. – David Berry Apr 08 '16 at 17:31

1 Answers1

-1

Check out this post: http://mydailyjava.blogspot.com/2013/06/advanced-java-generics-retreiving.html

It's actually exactly what you're looking for.

According to this, you'll just need to extend your Response class and then query the generic type of its super.

Gilad
  • 833
  • 1
  • 8
  • 13
  • @Marco13, you're right, but I didn't fill comfortable to cut and paste code from a blog post – Gilad Apr 18 '16 at 14:26
  • Sure (I'm not the downvoter, BTW), but maybe inserting this link as a comment would have been sufficient here.... – Marco13 Apr 18 '16 at 18:19