0

I'm writing a class that uses Jackson to serialize/deserialize objects. At compile time I don't know which is the type of the objects I will be serializing/deserializing.

I read a couple of articles and questions about the usages of TypeReference and TypeFactory, however I'm still having some issues.

My class looks like (removed some irrelevant code):

public class JsonSerializer<T> {

  private ObjectMapper objectMapper;
  private TypeReference typeReference;

  @PostConstruct
  public void init() {
      objectMapper = new ObjectMapper();
      typeReference = new TypeReference<T>(){ };
  }

  // ...


  public Object decode(CachedData cachedData) {
    try {
        return objectMapper.readValue(cachedData.getData(), typeReference);
    } // ...
  }
}

And this is the snippet of code I'm using to test this:

public class SerializerTest {

  private JsonSerializer<Clazz> serializer;
  private ObjectMapper objectMapper = new ObjectMapper();
  private static final String CHARSET = "UTF-8";
  private Clazz data = generateData(); // creates a simple object

  @BeforeMethod
  public void beforeMethod() {
      serializer = new JsonSerializer<Clazz>();
      serializer.init();
  }

  // ...

  public void shouldDecodeDataCorrectly() throws Exception {
    CachedData cachedData = new CachedData(0, 
      objectMapper.writeValueAsString(data).getBytes(CHARSET), 
      CachedData.MAX_SIZE);

    Object object = serializer.decode(cachedData);
    assertEquals(data, object);
  }

  public static class Clazz {
    // POJO with getters and setters
  }

}

As far as I could understand, using typeReference should be enough to let know the deserializer that the value read from the byte stream should be put in a Clazz object. However, the object returned by the decode() method is still a Map, which I understand is the default class for deserializing.

I tried also changing the decode() method to return return objectMapper.readValue(cachedData.getData(), objectMapper.getTypeFactory().constructType(typeReference)), but I'm getting the same problem.

I'm quite sure it's something regarding the way I pass the type into the serializer, but I haven't been able so far to fix it. Thanks.

manub
  • 3,990
  • 2
  • 24
  • 33
  • See http://stackoverflow.com/questions/17400850/is-jackson-really-unable-to-deserialize-json-into-a-generic-type – lisak Jul 01 '13 at 12:47

3 Answers3

1

It is not quite clear what exactly you are trying to do: much of the code does not make change. Specifically usage of TypeReference is wrong: you can not use type variables with it, type must be concrete.

However, perhaps solution is simpler than you thought. If you can keep track of original Class, just store that (or name from clazz.getName()), and pass that when deserializing.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • This is an implementation of Couchbase `Transcoder`, which is a way to serialize/deserialize data before putting them into the cache. I'm using Jackson to serialize/deserialize JSON as a String, which then I'll convert into `byte[]` and put into a `CachedData` object. I tried passing the Clazz.class object into the `Serializer` and changing the call to `objectMapper.readValue(cachedData.getData(), clazz);`, but it still doesn't work - I'll update the question. – manub Sep 11 '12 at 07:55
  • It worked now - I had to remove the default typing configuration I added before (after I posted the question). Thanks! – manub Sep 11 '12 at 08:23
0

Did you check @JsonTypeInfo ? Please have a look at it's documentation.

Chris
  • 5,584
  • 9
  • 40
  • 58
  • I cannot annotate the class - I think there should be another way of telling the 'ObjectMapper' to deserialize using a specific class? – manub Sep 10 '12 at 16:51
0

Based on the javadoc at http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/type/TypeReference.html here is how I would do it:

Remove the typeReference field from JsonSerializer

Modify the JsonSerializer.decode method to take a type reference as an argument

Pass the type reference as an argument

Object object = transcoder.decode(cachedData, new TypeReference<Clazz>() {});

BTW - "transcoder" should be "serializer"? Typo?

Guido Simone
  • 7,912
  • 2
  • 19
  • 21
  • While legal usage, use of `TypeReference` here is unnecessary. Rather should just pass `Clazz.class` directly. `TypeReference` is only useful when you need to specify generic type parameters for things like `Map`. – StaxMan Sep 10 '12 at 20:41
  • I cannot change the signature of the method, however I'm able to pass the Class object for class Clazz. Still not working though, please have a look at the other comment. (yeah, serializer and transcoder are quite equivalent, I changed the code when asking the question and I forgot to change it in all the places!) – manub Sep 11 '12 at 07:52