Advertisement
andreapdn

Jackson Library, Type Erasure & Java Reflection

May 30th, 2023 (edited)
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 4.47 KB | Help | 0 0
  1. import com.fasterxml.jackson.databind.ObjectMapper;
  2. import com.fasterxml.jackson.dataformat.csv.CsvMapper;
  3. import com.fasterxml.jackson.dataformat.csv.CsvSchema;
  4.  
  5. import java.io.IOException;
  6. import java.lang.reflect.ParameterizedType;
  7. import java.lang.reflect.Type;
  8. import java.util.ArrayList;
  9.  
  10. class Person {
  11.     Person() {}
  12.     Person(String name, String surname, int age) {
  13.         this.name = name;
  14.         this.surname = surname;
  15.         this.age = age;
  16.     }
  17.     public String name;
  18.     public String surname;
  19.     public int age;
  20. }
  21.  
  22. public class Marshalling {
  23.                                         // ↓ Know that using this is a very bad practise,
  24.     public static void main(String[] args) throws IOException { // but it's only for this example :)
  25.  
  26.         // CSV sample input:
  27.         String input = "name,surname,age\nAndrew,McDonalds,25\nLuke,Smith,78";
  28.         // Deserializing input:
  29.         ArrayList<Person> list = deserialize(input, new TypeReference<ArrayList<Person>>(){});
  30.  
  31.         // Printing out some fields:
  32.         System.out.println(list.get(0).name);
  33.         System.out.println(list.get(1).surname);
  34.         System.out.println(list.get(0).age + 3);
  35.  
  36.         // Serializing with a person more:
  37.         Person person = new Person("Rowena", "Chandler", 27);
  38.         list.add(person);
  39.         String output = serialize(list);
  40.         // Printing out the resulting CSV:
  41.         System.out.println(output);
  42.     }
  43.  
  44.     // If you test the following method, it works. But I had to change its signature
  45.     // (I replaced Class<> with Jackson TypeReference<>) and I'd have preferred to not do it!
  46.     // At the end of the file, there's why.
  47.     //                              ↓ Removed 'format' because I'm testing only CSV
  48.     public static <R> R deserialize(String content, TypeReference<R> type) throws IOException {
  49.         ObjectMapper mapper = new CsvMapper();
  50.  
  51.         // Getting the type contained in the TypeReference object:
  52.         Type parentType = type.getType(); // example of parentType: ArrayList<Person>
  53.         // Checking if parentType raw type is ArrayList:
  54.         if (!(parentType instanceof ParameterizedType) || !(ArrayList.class.equals(((ParameterizedType) parentType).getRawType()))) {
  55.             throw new IllegalArgumentException("R raw type must be java.util.ArrayList");
  56.         }
  57.         Type baseType = ((ParameterizedType) parentType).getActualTypeArguments()[0]; // example of baseType: Person
  58.  
  59.         // Reading and parsing CSV with Jackson
  60.         CsvSchema headerSchema = CsvSchema.emptySchema().withHeader();
  61.         return (R) mapper.readerFor(((Class<?>) baseType)) // You can see, here Jackson needs the base type!
  62.                 .with(headerSchema)
  63.                 .readValues(content).readAll(); // I should use a try-with-resources but for simplicity I omitted it
  64.     }
  65.     public static <T> String serialize(Object object) throws IOException {
  66.         ObjectMapper mapper = new CsvMapper();
  67.  
  68.         // Checking if object is an instance of ArrayList:
  69.         if (!(object instanceof ArrayList))
  70.             throw new IllegalArgumentException("R raw type must be java.util.ArrayList");
  71.         Class parentType = object.getClass(); // example of ParentType: ArrayList
  72.         // I think it's ABOVE that I LOOSE INFORMATION ON THE ARGUMENT TYPE
  73.  
  74.         // Here I tried in so many ways but it always prints out a cast exception:
  75.         // "TypeVariableImpl cannot be cast to class java.lang.Class" // FIXME
  76.         // (I've put the "fix me" only to highlight where my code is broken)
  77.         Type baseType = ((ParameterizedType) parentType.getGenericSuperclass()).getActualTypeArguments()[0];
  78.         System.out.println(baseType);
  79.  
  80.         // Writing CSV:
  81.         return mapper.writerFor((Class<?>) baseType).writeValueAsString(object); // You can see, here Jackson needs the base type!
  82.     }
  83.     public static class TypeReference<T> extends com.fasterxml.jackson.core.type.TypeReference<T> {}
  84. }
  85.  
  86. /*
  87.  * WHY DID I CHANGE SIGNATURE IN THE "deserialize" METHOD?
  88.  *
  89.  * It's because if it had the following signature:
  90.  * public static <R> R deserialize(String content, Class<R> type);
  91.  * I'd write in the main something like:
  92.  * deserialize(content, ArrayList<Person>.class) // ← but here there's a compile error
  93.  *                                               // because of type erasure
  94.  * I really don't know how use Class<> but not loosing information...
  95.  *
  96.  * Thanks again in advance, for any kind of help!
  97. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement