How to throw a custom exception from a custom deserializer - java

I have a class deserialized by my custom deserializer and I need to throw my custom exception.
public class MyClass {
}
public static void main(String[] args) {
try {
ObjectMapper mapper = new ObjectMapped();
MyClass myClass = mapper.readValue(json, MyClass.class);
} catch(Exception e) {
System.out.println(e); // JsonMappingException
System.out.println(e.getCause()); // null, but I need to get my CustomException.class
}
}
public class MyDeserializer extends StdDeserializer<MyClass> {
#Override
public MyClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
throw new CustomException("TestException", 1);
}
}
public class CustomException extends IOException {
private int code;
public CustomException(String message, int code) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}
What can I do to return my custom exception from the custom deserializer?

First you should add custom desializer to ObjectMapper or target Object like below
#JsonDeserialize(using = MyDeserializer.class)
public class MyClass {
}
getCause() return null because you already have caused Exception, check getCause implementation
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
shorty you don't need to getCause, e is your CustomException instance. Just do first step and remove getCause, it ll be fine.

Related

Jackson custom deserializer null codec

I have wrote custom deserializer for my type, which is represented as interface Attachment and there are two implementions of this interface Photo and Video.
When parsing I recognize them from json using discriminator field.
Now I'm facing problem when jp.getCodec() returns null, leading
to null pointer exception
Why this is happining and how to fix it?
public class AttachmentDeserializer extends StdDeserializer<Attachment> {
ObjectMapper objectMapper = new ObjectMapper();
public AttachmentDeserializer() {
this(null);
objectMapper.registerModule(new Jdk8Module());
}
public AttachmentDeserializer(Class<Attachment> t) {
super(t);
objectMapper.registerModule(new Jdk8Module());
}
#Override
public Attachment deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String type = node.get("type").asText();
switch (type) {
case "photo":
return new AttachmentPhoto(
node.get("t").asInt(),
objectMapper.readValue(node.get("photo").traverse(), Photo.class));
case "video":
return new AttachmentVideo(
node.get("t").asInt(),
objectMapper.readValue(node.get("video").traverse(), Video.class));
default:
throw ctxt.weirdStringException("type", Attachment.class, "Unknown discriminator");
}
}
}
The attachmentPhoto code:
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class AttachmentPhoto implements Attachment {
private Photo photo;
public Attachments what() {
return Attachments.ATTACHMENT_PHOTO;
}
public String getDiscriminator() {
return "photo";
}
public AttachmentPhoto() {}
public AttachmentPhoto(Photo photo) {
this.photo = photo;
}
public Photo getPhoto() {
return this.photo;
}
public AttachmentPhoto setPhoto(Photo v) {
this.photo = v;
return this;
}
public boolean isAttachmentPhoto() {
return true;
}
public AttachmentPhoto asAttachmentPhoto() {
return this;
}
public boolean isAttachmentVideo() {
return false;
}
public AttachmentVideo asAttachmentVideo() {
throw new IllegalStateException("Not a $stName: " + this);
}
#Override
public boolean equals(Object thatObj) {
if (this == thatObj) return true;
if (!(thatObj instanceof AttachmentPhoto)) return false;
AttachmentPhoto that = (AttachmentPhoto) thatObj;
return this.photo.equals(that.photo);
}
#Override
public String toString() {
return "AttachmentPhoto{" + "photo=" + this.photo + '}';
}
}
Your default constructor looks very suspicious for two reasons, first it calls the second constructor with null class type which then passes the null type to the superclass hence the overridden generic method is messed up when this constructor is used. Secondly, it does no useful work since it already calls the other constructor that initializes objectMapper. You should remove the first constructor and remain with just the typed one and initialize your deserializer using that.

How to use both JsonTypeInfo and de/serializers for custom handling of polymorphic subtypes?

I have a situation where I need to customize the serialization/deserialization of some JSON. I have simplified this into a readable example. I have a Container class that holds objects implementing MyInterface. In my example ClassA, ClassB, IntegerHolder and StringHolder implement the interface. By adding the #JsonTypeInfo annotation to my interface (and container):
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
and registering types names for each class, I can successfully read/write these to/from this JSON:
{"type":"Container","items":
[ {"type":"classA","aValue":"AAA"},
{"type":"classB","bValue":"BBB"},
{"type":"intHolder","value":123},
{"type":"stringHolder","value":"abc"} ] }
That is all very nice :) My problem is that I want to customize the serialization of the intHolder and stringHolder because they are just wrappers around native types. My JSON will be frequently edited by hand and the primitive types will be used a LOT. So I want to simplify the JSON to:
{"type":"Container","items":
[ {"type":"classA","aValue":"AAA"},
{"type":"classB","bValue":"BBB"},
123,
"abc" ] }
I have written a Serializer and Deserializer (extending StdSeralizer and StdDeserializer), put them in a SimpleModule and registered it with the mapper (as illustrated here on SO) and in isolation, it works well. By that, I mean that I can serialize/deserialize the IntegerHolder and StringHolder if they are the only objects in the container, and then only if I remove the #JsonTypeInfo annotation from the interface. If I do not, then I get this failure while writing to JSON:
[main] ERROR MyTests - can't write the Container
com.fasterxml.jackson.databind.JsonMappingException: Type id handling not implemented for type MyInterface (by serializer of type MyTests$MyInterfaceSerializer) (through reference chain: Container["items"])
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1047)
at com.fasterxml.jackson.databind.JsonSerializer.serializeWithType(JsonSerializer.java:142)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serializeTypedContents(ObjectArraySerializer.java:316)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serializeContents(ObjectArraySerializer.java:217)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serialize(ObjectArraySerializer.java:201)
at com.fasterxml.jackson.databind.ser.std.ObjectArraySerializer.serialize(ObjectArraySerializer.java:25)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:552)
at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3387)
at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2747)
at MyTests.testItemSerializationDeserializationEquality(MyTests.java:51)
at MyTests.testSerialization(MyTests.java:41)
But of course, with the #JsonTypeInfo removed, Jackson doesn't know how to deserialize ClassA and ClassB...so that fails while reading the JSON with:
[main] INFO MyTests - {"type":"Container","items":[{"aValue":"AAA"},{"bValue":"BBB"},123,"abc"]}
[main] ERROR MyTests - can't read the Container
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of MyInterface, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
at [Source: java.io.ByteArrayInputStream#37883b97; line: 1, column: 45] (through reference chain: Container["items"]->Object[][0])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:857)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:139)
at MyTests$MyInterfaceDeserializer.deserialize(MyTests.java:163)
at MyTests$MyInterfaceDeserializer.deserialize(MyTests.java:139)
I feel like Jackson can do it and I'm close to getting Jackson configured to serialize/deserialize both sets of classes, but so far my attempts have not been fruitful.
Any pointers to get me going in the right direction would be most appreciated...thanks in advance!
Here are the 7 classes in my test example:
MyInterface.java
import com.fasterxml.jackson.annotation.*;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface MyInterface
{
}
Container.java
import com.fasterxml.jackson.annotation.*;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public class Container
{
public Container()
{
}
public Container(MyInterface... items)
{
this.items = items;
}
public MyInterface[] getItems()
{
return items;
}
public void setItems(MyInterface[] items)
{
this.items = items;
}
#Override
public boolean equals(Object obj)
{
for (int i = 0; i < items.length; i++)
if (!(items[i].equals(((Container)obj).items[i])))
return false;
return true;
}
private MyInterface[] items;
}
MyTests.java
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.deser.std.*;
import com.fasterxml.jackson.databind.jsontype.*;
import com.fasterxml.jackson.databind.module.*;
import com.fasterxml.jackson.databind.node.*;
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.ser.std.*;
import org.junit.*;
import org.slf4j.*;
import java.io.*;
public class MyTests
{
#Test
public void testSerialization()
{
ClassA a = new ClassA();
a.setaValue("AAA");
ClassB b = new ClassB();
b.setbValue("BBB");
IntegerHolderClass int_holder = new IntegerHolderClass();
int_holder.setValue(123);
StringHolderClass string_holder = new StringHolderClass();
string_holder.setValue("abc");
// Testing with ONLY the non-customized classes works fine with the #JsonTypeInfo annotation on MyInterface
// if the custom de/serializers are not registered via the module
// testItemSerializationDeserializationEquality(new Container(a, b), Container.class);
// Testing with ONLY the customized classes works fine with the custom de/serializers registered via the module
// if the #JsonTypeInfo annotation on MyInterface is removed
// testItemSerializationDeserializationEquality(new Container(int_holder, string_holder), Container.class);
// This variation tests them all together...doesn't work under either scenario
testItemSerializationDeserializationEquality(new Container(a, b, int_holder, string_holder), Container.class);
}
private void testItemSerializationDeserializationEquality(Object original, Class expected_super_type)
{
ObjectMapper mapper = createMapper();
ByteArrayOutputStream outstream = new ByteArrayOutputStream();
try
{
mapper.writeValue(outstream, original);
outstream.flush();
}
catch (IOException e)
{
LOG.error("can't write the " + original.getClass().getSimpleName(), e);
}
LOG.info(outstream.toString());
Object copy = null;
try
{
copy = mapper.readValue(new ByteArrayInputStream(outstream.toByteArray()), expected_super_type);
}
catch (Exception e)
{
LOG.error("can't read the " + original.getClass().getSimpleName(), e);
}
Assert.assertNotNull(copy);
Assert.assertTrue(copy.equals(original));
}
private ObjectMapper createMapper()
{
ObjectMapper mapper = new ObjectMapper();
mapper.registerSubtypes(new NamedType(ClassA.class, "classA"));
mapper.registerSubtypes(new NamedType(ClassB.class, "classB"));
mapper.registerSubtypes(new NamedType(IntegerHolderClass.class, "intHolder"));
mapper.registerSubtypes(new NamedType(StringHolderClass.class, "stringHolder"));
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier()
{
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
{
if (MyInterface.class.isAssignableFrom(beanDesc.getBeanClass()))
return new MyInterfaceDeserializer(deserializer);
return deserializer;
}
});
module.setSerializerModifier(new BeanSerializerModifier()
{
#Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer)
{
if (MyInterface.class.isAssignableFrom(beanDesc.getBeanClass()))
return new MyInterfaceSerializer(serializer);
return serializer;
}
});
mapper.registerModule(module);
return mapper;
}
static class MyInterfaceSerializer extends StdSerializer<MyInterface> implements ResolvableSerializer
{
public MyInterfaceSerializer(JsonSerializer<?> def)
{
super(MyInterface.class);
_default = (JsonSerializer<MyInterface>) def;
}
#Override
public void serialize(MyInterface value, JsonGenerator jgen, SerializerProvider provider) throws IOException
{
if (value instanceof IntegerHolderClass)
jgen.writeNumber(((IntegerHolderClass) value).getValue());
else if (value instanceof StringHolderClass)
jgen.writeString(((StringHolderClass) value).getValue());
else
_default.serialize(value, jgen, provider);
}
#Override
public void resolve(SerializerProvider provider) throws JsonMappingException
{
}
private final JsonSerializer<MyInterface> _default;
}
static class MyInterfaceDeserializer extends StdDeserializer<MyInterface> implements ResolvableDeserializer
{
public MyInterfaceDeserializer(JsonDeserializer<?> def)
{
super(MyInterface.class);
_default = def;
}
#Override
public MyInterface deserialize(JsonParser parser, DeserializationContext context) throws IOException
{
TreeNode node = parser.getCodec().readTree(parser);
if (node instanceof TextNode)
{
StringHolderClass holder = new StringHolderClass();
holder.setValue(((TextNode) node).textValue());
return holder;
}
else if (node instanceof IntNode)
{
IntegerHolderClass holder = new IntegerHolderClass();
holder.setValue(((IntNode) node).intValue());
return holder;
}
return (MyInterface) _default.deserialize(parser, context);
}
#Override
public void resolve(DeserializationContext context) throws JsonMappingException
{
// ((ResolvableDeserializer)_default).resolve(context);
}
private final JsonDeserializer<?> _default;
}
final static Logger LOG = LoggerFactory.getLogger(MyTests.class);
}
ClassA.java
public class ClassA implements MyInterface
{
public String getaValue()
{
return _aValue;
}
public void setaValue(String aValue)
{
_aValue = aValue;
}
#Override
public boolean equals(Object obj)
{
return obj instanceof ClassA && _aValue.equals(((ClassA)obj)._aValue);
}
private String _aValue;
}
ClassB.java
public class ClassB implements MyInterface
{
public String getbValue()
{
return _bValue;
}
public void setbValue(String bValue)
{
_bValue = bValue;
}
#Override
public boolean equals(Object obj)
{
return obj instanceof ClassB && _bValue.equals(((ClassB)obj)._bValue);
}
private String _bValue;
}
StringHolderClass.java
public class StringHolderClass implements MyInterface
{
public String getValue()
{
return _value;
}
public void setValue(String value)
{
_value = value;
}
#Override
public boolean equals(Object obj)
{
return obj instanceof StringHolderClass && _value.equals(((StringHolderClass)obj)._value);
}
private String _value;
}
IntegerHolderClass.java
public class IntegerHolderClass implements MyInterface
{
public int getValue()
{
return _value;
}
public void setValue(int value)
{
_value = value;
}
#Override
public boolean equals(Object obj)
{
return obj instanceof IntegerHolderClass && _value.equals(((IntegerHolderClass)obj)._value);
}
private Integer _value;
}
Two options:
Custom deserializer for MyInterface and then you do not need the JsonTypeInfo - all the logic will be in the deserializer.
You can try and have IntegerHolder and StringHolder implement another interface let's say Holder and change the JsonTypeInfo annotation to:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl=Holder.class)
And for Holder.class specify a deserializer.

Jackson and generic type [duplicate]

I want to use jackson json library for a generic method as follows:
public MyRequest<T> tester() {
TypeReference<MyWrapper<T>> typeRef = new TypeReference<MyWrapper<T>>();
MyWrapper<T> requestWrapper = (MyWrapper<T>) JsonConverter.fromJson(jsonRequest, typeRef);
return requestWrapper.getRequest();
}
public class MyWrapper<T> {
private MyRequest<T> request;
public MyRequest<T> getRequest() {
return request;
}
public void setRequest(MyRequest<T> request) {
this.request = request;
}
}
public class MyRequest<T> {
private List<T> myobjects;
public void setMyObjects(List<T> ets) {
this.myobjects = ets;
}
#NotNull
#JsonIgnore
public T getMyObject() {
return myobjects.get(0);
}
}
Now the problem is that when I call getMyObject() which is inside the request object Jackson returns the nested custom object as a LinkedHashMap. Is there any way in which I specify that T object needs to be returned? For example: if I sent object of type Customer then Customer should be returned from that List?
This is a well-known problem with Java type erasure: T is just a type variable, and you must indicate actual class, usually as Class argument. Without such information, best that can be done is to use bounds; and plain T is roughly same as 'T extends Object'. And Jackson will then bind JSON Objects as Maps.
In this case, tester method needs to have access to Class, and you can construct
JavaType type = mapper.getTypeFactory().
constructCollectionType(List.class, Foo.class)
and then
List<Foo> list = mapper.readValue(new File("input.json"), type);
'JavaType' works !!
I was trying to unmarshall (deserialize) a List in json String to ArrayList java Objects and was struggling to find a solution since days.
Below is the code that finally gave me solution.
Code:
JsonMarshallerUnmarshaller<T> {
T targetClass;
public ArrayList<T> unmarshal(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper.getDeserializationConfig()
.withAnnotationIntrospector(introspector);
mapper.getSerializationConfig()
.withAnnotationIntrospector(introspector);
JavaType type = mapper.getTypeFactory().
constructCollectionType(
ArrayList.class,
targetclass.getClass());
try {
Class c1 = this.targetclass.getClass();
Class c2 = this.targetclass1.getClass();
ArrayList<T> temp = (ArrayList<T>)
mapper.readValue(jsonString, type);
return temp ;
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null ;
}
}
I modified rushidesai1's answer to include a working example.
JsonMarshaller.java
import java.io.*;
import java.util.*;
public class JsonMarshaller<T> {
private static ClassLoader loader = JsonMarshaller.class.getClassLoader();
public static void main(String[] args) {
try {
JsonMarshallerUnmarshaller<Station> marshaller = new JsonMarshallerUnmarshaller<>(Station.class);
String jsonString = read(loader.getResourceAsStream("data.json"));
List<Station> stations = marshaller.unmarshal(jsonString);
stations.forEach(System.out::println);
System.out.println(marshaller.marshal(stations));
} catch (IOException e) {
e.printStackTrace();
}
}
#SuppressWarnings("resource")
public static String read(InputStream ios) {
return new Scanner(ios).useDelimiter("\\A").next(); // Read the entire file
}
}
Output
Station [id=123, title=my title, name=my name]
Station [id=456, title=my title 2, name=my name 2]
[{"id":123,"title":"my title","name":"my name"},{"id":456,"title":"my title 2","name":"my name 2"}]
JsonMarshallerUnmarshaller.java
import java.io.*;
import java.util.List;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
public class JsonMarshallerUnmarshaller<T> {
private ObjectMapper mapper;
private Class<T> targetClass;
public JsonMarshallerUnmarshaller(Class<T> targetClass) {
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper = new ObjectMapper();
mapper.getDeserializationConfig().with(introspector);
mapper.getSerializationConfig().with(introspector);
this.targetClass = targetClass;
}
public List<T> unmarshal(String jsonString) throws JsonParseException, JsonMappingException, IOException {
return parseList(jsonString, mapper, targetClass);
}
public String marshal(List<T> list) throws JsonProcessingException {
return mapper.writeValueAsString(list);
}
public static <E> List<E> parseList(String str, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(str, listType(mapper, clazz));
}
public static <E> List<E> parseList(InputStream is, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(is, listType(mapper, clazz));
}
public static <E> JavaType listType(ObjectMapper mapper, Class<E> clazz) {
return mapper.getTypeFactory().constructCollectionType(List.class, clazz);
}
}
Station.java
public class Station {
private long id;
private String title;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return String.format("Station [id=%s, title=%s, name=%s]", id, title, name);
}
}
data.json
[{
"id": 123,
"title": "my title",
"name": "my name"
}, {
"id": 456,
"title": "my title 2",
"name": "my name 2"
}]

Improve class using generics

I have a lot of identical classes. I want to improve it using generics:
public class PingInitializer extends AbstractHandler implements DataWarehouseInitializer<PingInteraction, PingInvocation> {
#Handler
#Override
public PingInteraction handle(Message message) throws IOException, MessageException {
checkMessageIsNotNull(message);
PingInvocation invocation = construct(message.getBody().toString());
l.debug("handling in initializer... {}", invocation);
return new PingInteraction(invocation);
}
public PingInvocation construct(String message) throws IOException, MessageException {
ObjectMapper mapper = new ObjectMapper();
PingInvocation invocation;
try {
invocation = mapper.readValue(message, PingInvocation.class);
} catch (IOException e) {
throw new MessageException("Can't deserialize message", e);
}
return invocation;
}
}
I want to create new abstract class AbstractInitializer and all child class just have to specify generic type:
public abstract class AbstractInitializer<INTERACTION, INVOCATION> extends AbstractHandler {
#Handler
public INTERACTION handle(Message message) throws IOException, MessageException {
checkMessageIsNotNull(message);
INVOCATION invocation = construct(message.getBody().toString());
l.debug("handling in initializer... {}", invocation);
return **new INTERACTION(invocation)**; //HERE!
}
public INVOCATION construct(String message) throws IOException, MessageException {
ObjectMapper mapper = new ObjectMapper();
INVOCATION invocation;
try {
invocation = mapper.readValue(message, **INVOCATION.class** /*<- and HERE */);
} catch (IOException e) {
throw new MessageException("Can't deserialize message", e);
}
return invocation;
}
}
But I have two compilers error and don't know how to bypass this issue. I mark them in code
Java generics are implemented with type erasure for compatibility reasons, which ultimately means you cannot use a mere type parameter to instantiate a new object.
What you'll need is to modify AbstractInitializer a bit...
private final Class<INTERACTION> interactionType;
private final Class<INVOCATION> invocationType;
private final Constructor<INTERACTION> interactionConstructor;
public AbstractInitializer(final Class<INTERACTION> interactionType,
final Class<INVOCATION> invocationType) throws NoSuchMethodException {
this.interactionType = interactionType;
this.invocationType = invocationType;
interactionConstructor = interactionType.getConstructor(invocationType);
}
public INTERACTION handle(Message message) throws IOException, MessageException,
InvocationTargetException, InstantiationException, IllegalAccessException {
checkMessageIsNotNull(message);
INVOCATION invocation = construct(message.getBody().toString());
l.debug("handling in initializer... {}", invocation);
return interactionConstructor.newInstance(invocation);
}
public INVOCATION construct(String message) throws IOException, MessageException {
ObjectMapper mapper = new ObjectMapper();
INVOCATION invocation;
try {
invocation = mapper.readValue(message, invocationType);
} catch (IOException e) {
throw new MessageException("Can't deserialize message", e);
}
return invocation;
}
then, as an e.g.
public final class PingInitializer
extends AbstractInitializer<PingInteraction, PingInvocation> {
public PingInitializer() {
super(PingInteraction.class, PingInvocation.class);
}
}
or, you could ditch making it abstract and use it like...
public static GenericInitializer<A, B> createInitializer(final Class<A> a,
final Class<B> b) {
return new GenericInitializer<A, B>(a, b);
}
final GenericInitializer<PingInteraction, PingInvocation> pingInitializer
= createInitializer(PingInteraction.class, PingInvocation.class);
I believe this should all be possible. I probably made some dumb errors, however, given I typed this in the response box in a hurry.

Jackson and generic type reference

I want to use jackson json library for a generic method as follows:
public MyRequest<T> tester() {
TypeReference<MyWrapper<T>> typeRef = new TypeReference<MyWrapper<T>>();
MyWrapper<T> requestWrapper = (MyWrapper<T>) JsonConverter.fromJson(jsonRequest, typeRef);
return requestWrapper.getRequest();
}
public class MyWrapper<T> {
private MyRequest<T> request;
public MyRequest<T> getRequest() {
return request;
}
public void setRequest(MyRequest<T> request) {
this.request = request;
}
}
public class MyRequest<T> {
private List<T> myobjects;
public void setMyObjects(List<T> ets) {
this.myobjects = ets;
}
#NotNull
#JsonIgnore
public T getMyObject() {
return myobjects.get(0);
}
}
Now the problem is that when I call getMyObject() which is inside the request object Jackson returns the nested custom object as a LinkedHashMap. Is there any way in which I specify that T object needs to be returned? For example: if I sent object of type Customer then Customer should be returned from that List?
This is a well-known problem with Java type erasure: T is just a type variable, and you must indicate actual class, usually as Class argument. Without such information, best that can be done is to use bounds; and plain T is roughly same as 'T extends Object'. And Jackson will then bind JSON Objects as Maps.
In this case, tester method needs to have access to Class, and you can construct
JavaType type = mapper.getTypeFactory().
constructCollectionType(List.class, Foo.class)
and then
List<Foo> list = mapper.readValue(new File("input.json"), type);
'JavaType' works !!
I was trying to unmarshall (deserialize) a List in json String to ArrayList java Objects and was struggling to find a solution since days.
Below is the code that finally gave me solution.
Code:
JsonMarshallerUnmarshaller<T> {
T targetClass;
public ArrayList<T> unmarshal(String jsonString) {
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper.getDeserializationConfig()
.withAnnotationIntrospector(introspector);
mapper.getSerializationConfig()
.withAnnotationIntrospector(introspector);
JavaType type = mapper.getTypeFactory().
constructCollectionType(
ArrayList.class,
targetclass.getClass());
try {
Class c1 = this.targetclass.getClass();
Class c2 = this.targetclass1.getClass();
ArrayList<T> temp = (ArrayList<T>)
mapper.readValue(jsonString, type);
return temp ;
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null ;
}
}
I modified rushidesai1's answer to include a working example.
JsonMarshaller.java
import java.io.*;
import java.util.*;
public class JsonMarshaller<T> {
private static ClassLoader loader = JsonMarshaller.class.getClassLoader();
public static void main(String[] args) {
try {
JsonMarshallerUnmarshaller<Station> marshaller = new JsonMarshallerUnmarshaller<>(Station.class);
String jsonString = read(loader.getResourceAsStream("data.json"));
List<Station> stations = marshaller.unmarshal(jsonString);
stations.forEach(System.out::println);
System.out.println(marshaller.marshal(stations));
} catch (IOException e) {
e.printStackTrace();
}
}
#SuppressWarnings("resource")
public static String read(InputStream ios) {
return new Scanner(ios).useDelimiter("\\A").next(); // Read the entire file
}
}
Output
Station [id=123, title=my title, name=my name]
Station [id=456, title=my title 2, name=my name 2]
[{"id":123,"title":"my title","name":"my name"},{"id":456,"title":"my title 2","name":"my name 2"}]
JsonMarshallerUnmarshaller.java
import java.io.*;
import java.util.List;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
public class JsonMarshallerUnmarshaller<T> {
private ObjectMapper mapper;
private Class<T> targetClass;
public JsonMarshallerUnmarshaller(Class<T> targetClass) {
AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
mapper = new ObjectMapper();
mapper.getDeserializationConfig().with(introspector);
mapper.getSerializationConfig().with(introspector);
this.targetClass = targetClass;
}
public List<T> unmarshal(String jsonString) throws JsonParseException, JsonMappingException, IOException {
return parseList(jsonString, mapper, targetClass);
}
public String marshal(List<T> list) throws JsonProcessingException {
return mapper.writeValueAsString(list);
}
public static <E> List<E> parseList(String str, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(str, listType(mapper, clazz));
}
public static <E> List<E> parseList(InputStream is, ObjectMapper mapper, Class<E> clazz)
throws JsonParseException, JsonMappingException, IOException {
return mapper.readValue(is, listType(mapper, clazz));
}
public static <E> JavaType listType(ObjectMapper mapper, Class<E> clazz) {
return mapper.getTypeFactory().constructCollectionType(List.class, clazz);
}
}
Station.java
public class Station {
private long id;
private String title;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return String.format("Station [id=%s, title=%s, name=%s]", id, title, name);
}
}
data.json
[{
"id": 123,
"title": "my title",
"name": "my name"
}, {
"id": 456,
"title": "my title 2",
"name": "my name 2"
}]

Categories

Resources