Jackson mixin not being called - java

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.

Related

MapStruct: Object.class to Custom.class mapping

I'm a newbie with MapStruct, and need some help with that.
I have a Source class, with an attribute
Object input;
Which, in runtime, returns a custom object named TicketDetails.
Now, in my target class there is a attribute named,
MyTicket myTicket;
which, I need to map with an attribute of TicketDetails object.
For, better understanding, I'm writing the normal java code example below.
SourceClassModel sourceClassModel = new SourceClassModel();
TargetClassModel targetClassModel = new TargetClassModel();
//mapping
TicketDetails ticketDetails = (TicketDetails) sourceClassModel.getInput();
targetClassModel.setMyTicket(ticketDetails.getMyTicket);
Now, my question is, how to achieve this case using MapStruct?
Either on a used mapper (see #Mapper#uses()) or in a non-abstract method on the mapper itself - in case it is an abstract class and not an interface - define the mapping from Object to TicketDetails yourself:
TicketDetails asTicketDetails(Object details) {
return (TicketDetails) details;
}
The generated method for the conversion of SourceClassModel to TargetClassModel will then invoke that manually written method for converting the myTicket property.

how to use method canDeserialize ObjectMapper class from jackson API?

I want to use method canDeserialize, because at moment deserialization i want to get type class for apply at custom deserialization, as about next example :
public T deserialize(byte[] bytes) throws SerializationException {
bolean isAccount = this.objectMapper.canDeserialize(??????).
T t = null;
if(isAccount)
t = (T)this.objectMapper.readValue(bytes,Account.class);
else
t = (T) this.objectMapper.readValue(bytes, 0, bytes.length, new TypeReference<Object>(){});
return t;
}
In this case Account class have annotation #JsonDeserialize for a custom deserialization .
To directly answer your question, this is how you use the canDeserialize method:
final ObjectMapper mapper = new ObjectMapper();
mapper.canDeserialize(mapper.constructType(Bean.class));
Where Bean is the name of your Java class to be checked.
But wait, you are trying to solve the wrong problem. You are struggling with the logic for your method because it has not been designed properly. You are really asking too much of the Java runtime (and Jackson library), by trying to make them infer all the required information about the type to be instantiated (based on the parameterized return). To solve this you should include the class representing the type to be deserialized as a parameter to the method, greatly simplifying the logic:
public <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException,
JsonProcessingException {
return new ObjectMapper().readValue(bytes, clazz);
}
At this point you have probably realized that the method above provides no additional functionality over just calling ObjectMapper.readValue directly, so ... just do that! No need to define custom methods, just use ObjectMapper and you are good to go. Keep in mind that you do not need to do anything explicit to trigger custom deserialization of classes. The Jackson runtime automatically detects when a class has a custom deserializer and invokes it.

Spring/json: Convert a typed collection like List<MyPojo>

I'm trying to marshal a list: List<Pojo> objects via the Spring Rest Template.
I can pass along simple Pojo objects, but I can't find any documentation that describes how to send a List<Pojo> objects.
Spring is using Jackson JSON to implement the HttpMessageConverter. The jackson documentation covers this:
In addition to binding to POJOs and
"simple" types, there is one
additional variant: that of binding to
generic (typed) containers. This case
requires special handling due to
so-called Type Erasure (used by Java
to implement generics in somewhat
backwards compatible way), which
prevents you from using something like
Collection<String>.class (which does
not compile).
So if you want to bind data into a
Map<String,User> you will need to use:
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() {});
where TypeReference is only needed to
pass generic type definition (via
anynomous inner class in this case):
the important part is
<Map<String,User>> which defines type
to bind to.
Can this be accomplished in the Spring template? I took a glance at the code and it makes me thing not, but maybe I just don't know some trick.
Solution
The ultimate solution, thanks to the helpful answers below, was to not send a List, but rather send a single object which simply extends a List, such as: class PojoList extends ArrayList<Pojo>. Spring can successfully marshal this Object, and it accomplishes the same thing as sending a List<Pojo>, though it be a little less clean of a solution. I also posted a JIRA in spring for them to address this shortcoming in their HttpMessageConverter interface.
In Spring 3.2 there is now support for generic types using the new exchange()-methods on the RestTemplate:
ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);
Works like a charm!
One way to ensure that generic type parameters are included is to actually sub-class List or Map type, such that you have something like:
static class MyStringList extends ArrayList<String> { }
and return instance of that list.
So why does this make a difference? Because generic type information is retained in just a couple of places: method and field declarations, and super type declarations. So whereas "raw" List does NOT include any runtime type information, class definition of "MyStringList" does, through its supertype declarations.
Note that assignments to seemingly typed variables do not help: it just creates more compile-time syntactic sugar: real type information is only passed with Class instances (or lib-provided extensions thereof, like JavaType and TypeReference in Jackson's case).
Other than this, you would need to figure out how to pass Jackson either JavaType or TypeReference to accompany value.
If I read the docs for MappingJacksonHttpMessageConverter right, you will have to create and register a subclass of MappingJacksonHttpMessageConverter and override the getJavaType(Class<?>) method:
Returns the Jackson JavaType for the
specific class. Default implementation
returns
TypeFactory.type(java.lang.reflect.Type),
but this can be overridden in
subclasses, to allow for custom
generic collection handling. For
instance:
protected JavaType getJavaType(Class<?> clazz) {
if (List.class.isAssignableFrom(clazz)) {
return TypeFactory.collectionType(ArrayList.class, MyBean.class);
} else {
return super.getJavaType(clazz);
}
}
I have solved this problem by using the following configuration:
private static final String POJO_ARRAY_LIST = PojoArrayList.class.getCanonicalName();
#Bean
public HttpMessageConverter<Object> httpMessageConverter() {
HttpMessageConverter<Object> httpMessageConverter = new MappingJackson2HttpMessageConverter() {
#Override
protected JavaType getJavaType(Type type, #Nullable Class<?> contextClass) {
JavaType javaType;
if (type != null && POJO_ARRAY_LIST.equals(type.getTypeName())) {
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = objectMapper.getTypeFactory();
CollectionType collectionType = typeFactory.constructCollectionType(ArrayList.class, Pojo.class);
javaType = collectionType;
} else {
javaType = super.getJavaType(type, contextClass);
}
return javaType;
}
};
return httpMessageConverter;
}
where PojoArrayList is a final class that extends ArrayList<Pojo>.

Jackson JSON library: how to instantiate a class that contains abstract fields

I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info

Jackson not recognizing #JsonCreator annotation

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.

Categories

Resources