1

I'm trying to deserialize a JSON object that looks like this:

{"payload":"cGF5bG9hZA==",
 "verifier":{"id":"00000000-0000-0001-0000-000000000002",
             "accountId":"00000000-0000-0003-0000-000000000004"}}

with this call:

VerifiedPayload<VerifierType> verifiedPayload = new ObjectMapper().readValue(jsonVerifiedPayload, new TypeReference<VerifiedPayload<VerifierType>>() {});

and I'm getting the error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `tech.dashman.common.JSONable` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (String)"{"payload":"cGF5bG9hZA==","verifier":{"id":"00000000-0000-0001-0000-000000000002","accountId":"00000000-0000-0003-0000-000000000004"}}"; line: 1, column: 38] (through reference chain: tech.dashman.common.crypto.VerifiedPayload["verifier"])

    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1438)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:519)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:527)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:416)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1265)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:325)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3011)
    at tech.dashman.common.crypto.AsymEncryptedData.decrypt(AsymEncryptedData.java:82)

VerifiedPayload looks like this:

@Data
@NoArgsConstructor
@AllArgsConstructor
class VerifiedPayload<VerifierType extends JSONable> implements JSONable {
    private byte[] payload;
    private VerifierType verifier;
}

and VerifierType in this case is the class Verifier that looks like this:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Verifier implements JSONable {
    private UUID id;
    private UUID accountId;
}

It looks like it's trying to construct a JSONable instead of a VerifiedPayload<VerifierType>>.

What am I doing wrong here?

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622
  • Presumably, at the callsite of `readValue`, `VerifierType` is also a type variable, not an actual type (class, interface). For type variables, Jackson extracts their bounds and uses that as the target for deserialization. – Sotirios Delimanolis Sep 26 '17 at 17:52
  • Did you mean to use `Verifier`? `new TypeReference>(){}`? – Sotirios Delimanolis Sep 26 '17 at 17:52
  • @SotiriosDelimanolis: I think I see your point, but when deserializing I don't want to be tied to Verifier as it could be a different class that is being used as Verifier. – Pablo Fernandez Sep 26 '17 at 17:54
  • Jackson can't guess what you want though. Type erasure prevents propagation of type arguments. `VerifierType` is just a type **variable**. – Sotirios Delimanolis Sep 26 '17 at 17:55
  • @SotiriosDelimanolis: ok, but then, how do I deserialize a VerifiedPayload? – Pablo Fernandez Sep 26 '17 at 17:56
  • You can't. You need additional type information, ie. a concrete type instead of the `VerifierType`. (Alternatively, you could use polymorphic deserialization with `@JsonTypeInfo`, but then you'd need to control `JSONable` and the subtypes you want to deserialize to.) – Sotirios Delimanolis Sep 26 '17 at 17:58
  • The VerifierType needs to be a variable, otherwise my class cannot take different types of verifiers. That's the whole point of the change I'm doing. – Pablo Fernandez Sep 26 '17 at 17:59
  • Yeah, that won't work. The caller of `readValue` (or some intermediate) will need to propagate the exact type they want back, whether through a `Class` object or a `TypeReference` of their own. – Sotirios Delimanolis Sep 26 '17 at 18:02
  • My answer [here](https://stackoverflow.com/a/20803111/438154) is for Gson's `TypeToken`, but it works the same for Jackson's `TypeReference`. From the invocation you have in your code, Jackson can only extract `VerifierType` (not some other concrete type argument you passed in somewhere below in the call stack) and it can't do anything with `VerifierType` because its a type variable. – Sotirios Delimanolis Sep 26 '17 at 18:09
  • Or see [here](https://stackoverflow.com/questions/6846244/jackson-and-generic-type-reference). – Sotirios Delimanolis Sep 26 '17 at 18:15
  • You'll have to include some type info in your verifiers. The easiest way to do this is to make them extend a base class annotated with `@JsonTypeInfo`. – teppic Sep 26 '17 at 19:36

0 Answers0