Jackson not recognizing #JsonCreator annotation - java

I am currently using Jackson 1.4.2 and attempting deserialization of code values (unique identifiers for type information) that are passed from our UI back to the Java controllers (Servlets).
There are multiple types (e.g. ABCType, XYZType, etc.) that all extend from an AbstractType, but each concrete type has a static factory method that takes as a single parameter, a unique identifier, and returns the type object (name, associated types, description, valid acronyms, etc.) represented by that identifier. The static method within each concrete type (e.g. XYZType) is annotated with #JsonCreator:
#JsonCreator
public static XYZType getInstance(String code) {
.....
}
The problem that I am seeing though is an exception thrown by Jackson's mapper trying to deserialize the json to those types:
Caused by: org.codehaus.jackson.map.JsonMappingException: No default constructor found for type [simple type, class com.company.type.XYZtype]: can not instantiate from Json object.
What am I missing here of the #JsonCreator annotation to static factory methods (or is it to do with Jackson 1.4.2 struggling with the concrete types extending from an AbstractType?)?

The annotation #JsonCreator requires the annotation #JsonProperty. This Jackson wiki page gives little information but does offer sample code:
#JsonCreator
public Name(#JsonProperty("givenName") String g, #JsonProperty("familyName") String f)
{
givenName = g;
familyName = f;
}
You'll find a more detailed explanation at this blog post.
Your sample code should therefore look something like this:
#JsonCreator
public static XYZType getInstance(#JsonProperty("someCode") String code)
{
...
}

Problem is that Jackson only sees the declared base type, and does not know where to look for subtypes.
Since full polymorphic type handling was added in 1.5, what you need to do with 1.4 is to add factory method in the base class and dispatch methods from there.

Related

Passing TYPE annotations to methods instead of marker interfaces

[ANSWER EDIT]: Short answer is that what I'm looking to do isn't possible. My question is a little misleading. I learnt that the Marker Interface pattern is actually what I called the Marked Annotations in my question (since the annotation you're creating is actually an interface). And checks on that can only be made at runtime. So if you're looking to make a compile time check with annotations well it's just not possible. An empty interface is the only option. Check answer to see how to do it at runtime.
I'm trying to avoid using marker interfaces in favor of marked annotations. Basically I want a bunch of classes be marked with this annotation, and pass instances of those classes to methods that accept that type. Here is my code:
MARKER ANNOTATION:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Message {
}
CLASS:
#Message
public class MessageTypeA {
}
METHOD:
public class DatabaseWriter {
public void save(Message msg) {
//some code
}
}
CALLING CODE:
MessageTypeA msgA = new MessageTypeA();
DatabaseWriter writer = new DatabaseWriter();
writer.save(msgA);
However I get Error:(78, 23) java: incompatible types: MessageTypeA cannot be converted to Message
I'm not sure if what I'm doing is possible, but I read that marker interfaces can be replaced with marker annotations. Is it not possible in this case?
Thanks
The marker interface pattern is a way of adding metadata to your program types or obbjects that is readable in runtime.
See for example hibernate implementation of this pattern. Their insert method accepts a plain java.lang.Object, and is inside that method where the metadata from the various annotations is used.
So, following your example implementation, I'd go with something like this
public class DatabaseWriter {
public void save(Object msg) {
if (msg.getClass().isAnnotationPresent(Message.class)) {
//some code
}
}
}
In your example, MessageTypeA and Message are unrelated in the class hierarchy. A method call is legal only if the expression's type is a subtype of the formal parameter's type.
One way to establish a subtyping relationship is with an interface, as you already noted.
Another way to establish a subtyping relationship is with type qualifiers (expressed as type annotations).
TYPE QUALIFIER HIERARCHY:
#Message
|
#MessageTypeA
where #MessageTypeA is a subtype of #Message, and #Message means an unknown type of message. #Message is the default if no type annotation is written.
LIBRARY
public class DatabaseWriter {
public void save(Object msg) {
// some code that can run on any old message
}
public void saveA(#MessageTypeA Object msg) {
// some code that is specific to MessageTypeA
}
}
CLIENT
Object msg = ...;
#MessageTypeA Object msgA = ...;
DatabaseWriter writer = new DatabaseWriter();
writer.save(msg); // legal
writer.save(msgA); // legal
writer.saveA(msg); // compile-time error
writer.save(msgA); // legal
There is no run-time overhead or representation: the enforcement is done at compile time.
A tool that enables you to build pluggable type-checkers that enforce correct usage is the Checker Framework. (Disclaimer: I am a maintainer of the tool, but it is a regular part of the development toolchain at Amazon, Google, Uber, etc.)
You can define your own type system in a few lines of code. However, still consider using Java subtypes rather than type qualifiers.

Object deserializtion in Redisson for a class with only parameterized constructor

I've a java object(ComponentType.java) that I need to store in Redis. I'm using Redisson as client library. The object has an instance variable (ComponentType) which only has one private parameterized constructor. The ComponentType class has been generated using castor. In Redisson , the serialization part works fine but when I try to deserialize the object, I get the following exception
Exception in thread "main" org.redisson.client.RedisException: Unexpected exception while processing command
at org.redisson.command.CommandAsyncService.convertException(CommandAsyncService.java:324)
at org.redisson.command.CommandAsyncService.get(CommandAsyncService.java:167)
at org.redisson.RedissonObject.get(RedissonObject.java:75)
at org.redisson.RedissonMap.put(RedissonMap.java:256)
at tester.RedissonIPWCTaskTester.populateMap(RedissonIPWCTaskTester.java:67)
at tester.RedissonIPWCTaskTester.main(RedissonIPWCTaskTester.java:51)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.mae.component.valueobject.types.ComponentType]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 769] (through reference chain: com.mae.component.valueobject.ComponentVO["_type"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:256)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1134)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:298)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:168)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:135)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:120)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:91)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1021)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:493)
The exception is resolved when the constructor of ComponentType is manually modified as below
#JsonCreator
private ComponentType(#JsonProperty("type") int type, #JsonProperty("value") java.lang.String value) {
super();
this.type = type;
this.stringValue = value;
}
I would appreciate help with following questions
Is there a way to generate java classes using castor that supports annotations.
Any other serialization/deserialization technique I could use in Redisson client to support objects having parameterized constructor only.
Summary of my blog post:
With Java 8 you can optionally include constructor metadata in compiled code and Jackson can use it instead of requiring #JsonCreator and #JsonProperty.
To achieve that:
compile with passing -parameters to javac
include and register jackson-module-parameter-names
Jackson will then be able to use the non-annotated constructor generated by castor.

Jackson mixin not being called

I have already posted something similar but I still trying to zero in on my problem.
Thanks for bearing with me.
It would appear that jackson is not calling a mixin as it should and I can't tell why.
"Element" is an interface not a class. It is normally instantiated with a static factory call as shown in the mixin (below). The way I understand it, when jackson sees the interface: Element.class it should look up the mixin then execute the method that has the #JsonCreator annotation. None of this is happening. If it were, I would see output from the logger. Instead, as one can see in the error message (way below), jackson is trying to treat my interface as a class and can't.
Why isn't my mixin working?
Here's the mixin:
public class ElementMixin {
private static Logger log = LoggerFactory.getLogger(ElementMixin.class);
#JsonCreator
public static Element create() {
log.error("Element==>");
return FhirFactory.eINSTANCE.createElement();
}
}
Here's how I register it with the mapper:
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Element.class, ElementMixin.class);
Here's how I am running things:
// Instantiate my interface, put some data in and serialize.
Element ela = FhirFactory.eINSTANCE.createElement();
ela.setId("CBAEL");
StringWriter writer = new StringWriter();
mapper.writeValue(writer, ela);
// Now try to deserialize into a new instance.
StringReader reader = new StringReader(writer.toString());
Element elp = mapper.readValue(reader, Element.class);//Error thrown
assertNotNull(elp);
The error:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct
instance of fhir.Element, 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.StringReader#4fe533ff; line: 1, column: 1] at
com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:255)
at
com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1007)
at
com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:150)
at
com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3807)
at
com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2844)
at
gov.nist.forecast.fhir.resources.IndexResourceTest.testParametersJSON(IndexResourceTest.java:173)
Mix-ins only associate annotations; they can not and do not add any fields or methods -- no bytecode generation or manipulation is added. So while you can add annotations to indicate methods that already exist in target (including static factory methods) should be used, nothing (aside from annotations) defined in mix-in will ever get called or used.

How to use Custom type for #PathParam?

I want to use non spring bean class object as parameter for jersey web service class method. But it is giving missing dependency error at build time.
My code is:
#Component
#Path("/abcd")
public class ActorServiceEndpoint {
#POST
#Path("/test/{nonspringBean}")
#Produces(MediaType.APPLICATION_XML)
public void addActor(#PathParam("nonspringBean") MyNonSpringBeanClass nonspringBean){
}
}
The thing is path parameters come in String form. As per the specification, if we want the have a custom type be injected as a #PathParam, the custom class, should have one of three things:
A public static valueOf(String param) that returns the type
A public static fromString(String param) that returns the type
Or a public constructor that accepts a String
Another option implement a ParamConverter. You can see an example here.
If you don't own the class (it's a third-party class that you can't change) then your only option is to use the ParamConverter/ParamConverterProvider pair.
In either of these cases you'll want to construct the instance accordingly by parsing the String either in the constructor or in one of the above mentioned methods. After doing this, the custom type can be made a method parameter with the annotation.
The same holds true for other params, such as #FormParam, #HeaderParam, #QueryParam, etc.
It would help if you gave a bit more details of the error you're getting, but I see two problems with your code snippet:
The correct Spring annotation is #PathVariable, #PathParam is probably from another package. This doesn't apply as I guess you're using JAX-RS, not Spring annotations.
I'm not sure what converters are applied to path variables, but in any case it would need to have one for MyNonSpringBeanClass. I would take a String parameter and then instantiate MyNonSpringBeanClass myself in the function body.

Problems with GWT and Enum

I have an enum in the client part of a GWT application and I am getting an exception when I try to run it that is related to serialization problems. Am I doing anything wrong? I read that enums are supported by GWT and I am using the last version.
The enum:
public enum AnEnum implements Serializable {
ITEM_A("Item a description"), ITEM_B("Item b description");
private String description;
private AnEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The exception:
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeWithCustomSerializer(ServerSerializationStreamWriter.java:742)
... 47 more
Caused by: com.google.gwt.user.client.rpc.SerializationException: Type '(...).client.(...).AnEnum' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = ITEM_A
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:610)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter.writeObject(AbstractSerializationStreamWriter.java:129)
at com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase.serialize(Collection_CustomFieldSerializerBase.java:43)
at com.google.gwt.user.client.rpc.core.java.util.LinkedList_CustomFieldSerializer.serialize(LinkedList_CustomFieldSerializer.java:36)
... 52 more
Add IsSerializable interface, a default scoped no-arg constructor, and make sure its in one of the paths listed in the source tags in your gwt.xml file. <source path="client">
I really think the third suggestion is the issue; I remember having this issue before and it was because I had a dto outside the source paths.
You can have multiple source tags.
<source path="common" />
<source path="client" />
One pattern is to put persisted objects directly under com.mysite.common, and mashups of persisted items that get transferred over the wire in com.mysite.common.dto, and of course the client gui code is in client.
package com.mysite.client;
import java.io.Serializable;
import com.google.gwt.user.client.rpc.IsSerializable;
public enum AnEnum implements Serializable, IsSerializable {
ITEM_A("Item a description"), ITEM_B("Item b description");
private String description;
AnEnum() {
}
AnEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
You can try this check list:
Verify that the class has a default constructor (without arguments)
Verify that the class implements Serializable or IsSerializable or
implements an Interface that extends Serializable or extends a class
that implement Serializable
Verify that the class is in a client.* package or …
Verify, if the class is not in client.* package, that is compiled in
your GWT xml module definition. By default
is present. If your class is in another package you have to add it
to source. For example if your class is under domain.* you should
add it to xml as . Be aware that the class
cannot belong to server package! More details on GWT page: http://code.google.com/webtoolkit/doc/latest/DevGuideOrganizingProjects.html#DevGuideModuleXml
If you are including the class from another GWT project you have to
add the inherits to your xml module definition. For example if your
class Foo is in the package com.dummy.domain you have to add
to the module definition.
More details here: http://code.google.com/webtoolkit/doc/latest/DevGuideOrganizingProjects.html#DevGuideInheritingModules
If you are including the class from another GWT project released as
a jar verify that the jar contains also the source code because GWT
recompile also the Java source for the classes passed to the Client.
Font: http://isolasoftware.it/2011/03/22/gwt-serialization-policy-error/
i think you need a no arg constructor.
I been studying above to solve some GWT code written in 2008, when upgraded to GWT SDK 2.4.0 (with latest gxt*.jar) gives me:
[WARN] adempiereService: An IncompatibleRemoteServiceException was thrown while processing this call.
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: com.google.gwt.user.client.rpc.SerializationException: Type 'org.idempiere.ui.gwt.client.util.AdempiereGXTUtil$LoginStage' was not included in the set of types which can be deserialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be deserialized.
at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:315)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:206)
...
Caused by: com.google.gwt.user.client.rpc.SerializationException: com.google.gwt.user.client.rpc.SerializationException: Type 'org.idempiere.ui.gwt.client.util.AdempiereGXTUtil$LoginStage' was not included in the set of types which can be deserialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be deserialized.
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:581)
That notorious class is as follows (edited to follow ideas given in this thread):
public class AdempiereGXTUtil {
public enum LoginStage implements IsSerializable, Serializable {
LOGOUT,
LOGIN,
ISLOGGEDIN,
ROLES,
WRONGUSER,
WRONGROLE;
LoginStage(){
}
};
}
Thinking about Andrej's answer to add type to white-list but enum is not some new myType, right? Anyway here is some reference in the codebase (non-relevant fields removed):
public interface AdempiereService extends RemoteService {
public static final String SERVICE_URI = "adempiereService";
public static class Util {
public static AdempiereServiceAsync getInstance() {
AdempiereServiceAsync instance = (AdempiereServiceAsync) GWT
.create(AdempiereService.class);
return instance;
}
}
...
public LoginStage getLoginStage();
with:
public interface AdempiereServiceAsync {
...
public void getLoginStage(AsyncCallback<LoginStage> callback);
Originally the AdempiereGXTUtil did not implement IsSerializable, Serializable nor has empty constructor but putting them in above, and cleaning out project in Eclipse does not change the same errors. Eclipse version used is Indigo on Java 1.6 in a Mac Lion environment. Hoping to get more from this thread, which by the way is amazing in its technical depth.
Only names of enumeration constants are serialized by GWT's RPC. Field values are not serialized.
GWT:Server Communication:Serializable Types
In this case, Enum cannot be in the class. You have to create an external Enum.
In Gwt 2.9, I also had a "was not included in the set of types which can be serialized" error on a class that contained a field of type java.util.EnumSet of a custom enum class.
It turned out the issue was not my custom enum, but the EnumSet itself. After replacing the EnumSet with a HashSet or LinkedHashSet, serialization worked.
Maybe this is related to this issue: https://github.com/gwtproject/gwt/issues/3319
See http://www.gwtproject.org/doc/latest/DevGuideServerCommunication.html#DevGuideSerializableTypes:
A type is serializable and can be used in a service interface if one
of the following is true:
The type is primitive, such as char, byte, short, int, long, boolean,
float, or double. The type an instance of the String, Date, or a
primitive wrapper such as Character, Byte, Short, Integer, Long,
Boolean, Float, or Double. The type is an enumeration. Enumeration
constants are serialized as a name only; none of the field values are
serialized. The type is an array of serializable types (including
other serializable arrays). The type is a serializable user-defined
class. The type has at least one serializable subclass. The type has a
Custom Field Serializer
In your case the type is an enumeration, so it is meant to be serializable.
These days GWT has 2 serialisation policies: LegacySerializationPolicy and StandardSerializationPolicy.
The LegacySerializationPolicy has an issue serialising enums unless they implement IsSerializable.
Normally StandardSerializationPolicy will be used unless the SerializationPolicyProvider cannot find the *.gwt.rpc file for some reason. If it cannot find the *.gwt.rpc file then it will write a warning that you will most likely get serialisation issues so I would suggest that you start looking in the error log to see if it had issues finding the *.gwt.rpc file and if so fix that.
Also on a side note you should not be mutating Enums, I recall that Joshua Bloch writes about that in his book Effective Java.
a) You definitely need a no-op constructor for serialization.
b) You can either extend GWT' IsSerializable class or, if you want to use Java's Serialization interface you must setup a policy to allow this. There's a post related to this at How do I add a type to GWT's Serialization Policy whitelist?. Also, check the GWT sight for more information on IsSerializable vs. Serializable.

Categories

Resources