1

I have done this in Java before but don't have access to the source code that did it nor do I remember the search phrase that got me the answer in the first place!

I have a method that needs to work like follows..

public <T> List<T> get(String json) {
 return JsonSerializer.deserialize(json, Class<List<T>>);
}

Every answer I seem to find always assumes I have an List object that I can then use reflection on but in my case I do not. Any ideas?

  • Can't you use `List.class`? –  Feb 05 '20 at 14:57
  • Do you mean `List.class`? – QBrute Feb 05 '20 at 14:57
  • No, that doesn't provide any type information to the deserializer about the object its trying to deserialize. – Mark Cesnik Feb 05 '20 at 14:59
  • 4
    A `List` doesn't carry any type information at runtime anyway. –  Feb 05 '20 at 15:00
  • @MarkCesnik Maybe that's what you're looking for? https://stackoverflow.com/a/30007018/759049 – Pateman Feb 05 '20 at 15:01
  • This code implies I have a handle to the class already which I don't nor do I have a handle to an object to call getClass() from. – Mark Cesnik Feb 05 '20 at 15:10
  • There is no input to the `get` method that says what `T` is. If you need this information, you're going to have to change the signature and make callers pass it in, and then you're going to have to pass it to the deserializer. The thing you remember doing before must have been different from what you want here. – Matt Timmermans Feb 05 '20 at 15:32
  • See https://en.m.wikipedia.org/wiki/Type_erasure and https://docs.oracle.com/javase/tutorial/java/generics/erasure.html which explain why this is impossible. – kaya3 Feb 05 '20 at 15:33
  • @MattTimmermans you are correct. I actually do have a handle to Class that can and will most likely need to be passed in. – Mark Cesnik Feb 05 '20 at 15:41

2 Answers2

1

You have 2 separate issues here.

Issue the first: The <T> in your signature is entirely eliminated. Therefore what you want (have the code write, say, List<Integer> x = get(someJsonHere); take the notion 'hey, this code wants explicitly a list of INTEGERS, and use that at runtime to deserialize the json' is impossible as that information does not exist at runtime. The only way is that the API'd be, for example: List<Integer> x = getListOf(Integer.class, someJsonHere) or List<Integer> x = getIntList(someJsonHere).

Issue the second:

For the same reason as above, the json deserializer doesn't know what to do either. java.lang.Class instances represent classes, not the generics after them; it is not possible to represent the idea of a 'list of strings' with a java.lang.Class object. Only 'a list'.

Therefore, just like your proposed API does not work, surely whatever 'JsonDeserializer' is here, its author made something that isn't fundamentally broken and therefore offers you a way to tell it that you want a list of integers or strings or whatnot specifically.

There are 3 common ways:

[1] pass non-genericsed types whose fields are generified, so:

public class YourThing {
    List<String> listOfStrings;
}

and pass YourThing.class.

[2] more parameters!

JsonDeserializer.deserialize(yourJson, List.class, String.class);

[3] super type tokens. Looks funky, but this is legal java:

JsonDeserializer.deserialize(yourJson, new JsonDeserializerType<List<String>>() {}); // Note the trailing {}. Required.

Check the docs of the json deserializer; it'll tell you which of these strategies it offers and how to use it specifically.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
0

You can just ignore generics in this case and use List.class and it will work just fine.

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(new JavaTimeModule());
objectMapper.enableDefaultTyping();
ObjectReader reader = objectMapper.reader();
List<T> data = (List<T>)(objectReader.forType(List.class).readValue(s));
Michael Gantman
  • 7,315
  • 2
  • 19
  • 36
  • I mean, there will be no type safety verification at the time when the method is run, as the generics would normally ensure is the latest time the type safety is verified. But such type safety cannot be verified with the method signature required – kumesana Feb 05 '20 at 15:04
  • This does not work. The serializer has to know about the class T so it can correctly deserialize the json array into a list of actual objects. Doing it this way create a list of Map objects. – Mark Cesnik Feb 05 '20 at 15:08
  • What deserializer do you use? If you switch to Json Jackson library it will work. I updated the answer with the code sample – Michael Gantman Feb 05 '20 at 15:27
  • Your example creates a List of HashMap not of type T. So yes, it does deserialize but when I try to access the data it dies horribly. – Mark Cesnik Feb 05 '20 at 15:38
  • I just updated my answer with the line `objectMapper.enableDefaultTyping();` See if it helps – Michael Gantman Feb 05 '20 at 15:46
  • Actually, my suggestion in the previous comment won't help, but, may be you can deserialize it with hashmap and then have all your implementations of T type have a constructor that take hushmap – Michael Gantman Feb 05 '20 at 15:50