I have following value holder class for users:
package entities;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
import javax.annotation.Nullable;
#Value.Immutable
#JsonSerialize(as = ImmutableUser.class)
#JsonDeserialize(as = ImmutableUser.class)
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public interface User {
String getUsername();
String getEmail();
#Nullable String getPassword();
#Nullable String getEncodedPassword();
}
Immutable final implementation of this value holder is being generated during compilation:
#SuppressWarnings("all")
#ParametersAreNonnullByDefault
#Generated({"Immutables.generator", "User"})
#Immutable
public final class ImmutableUser implements User {
Serialized instance of Immutable
{"#class":"entities.ImmutableUser$Json","username":"testuser","email":"123#gmail.com","password":null,"encodedPassword":null}
The problem is that deserialization of this JSON fails with following error:
java.lang.IllegalArgumentException: Class entities.ImmutableUser$Json is not assignable to entities.User
at com.fasterxml.jackson.databind.JavaType._assertSubclass(JavaType.java:466)
at com.fasterxml.jackson.databind.JavaType.narrowBy(JavaType.java:149)
at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:315)
at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:64)
... 38 more
Why does #class property in JSON for serialized instance have value "entities.ImmutableUser$Json" instead of "entities.ImmutableUser"? Is it because of fact that the class is final?
Is there any other way to serialize such classes and to avoid problems during deserialization?
Found out that the problem was caused by generated class. Turns out
that such classes should be marshaled using specific classes:
immutables.github.io/site1.x/json.html
The http://immutables.github.io/site1.x/json.html is referring to the older version of documentation and is quite irrelevant if you use Immutables v2.0 and up. In your case you're facing an already fixed issue (similar to https://github.com/immutables/immutables/issues/175). Try upgrading to Immutables v2.1 to get it resolved.
In nutshell, Jackson have feature annotation #JsonValue to substitute object during serialization. Unfortunately as we found out that it does not play well with other functionality as #JsonTypeInfo and #JsonSubTypes. See https://github.com/FasterXML/jackson-databind/issues/937.
Version 2.1 of Immutables no longer used #JsonValue, so it should work now. If not, please report it as a bug to https://github.com/immutables/immutables/issues
Related
I am trying to use JAXB with fields of the LocalDateTime type. I wrote an adapter to handle conversion:
public class LocalDateTimeXmlAdapter extends XmlAdapter<String, LocalDateTime> {
#Override
public String marshal(LocalDateTime arg0) throws Exception {
return arg0.toString();
}
#Override
public LocalDateTime unmarshal(String arg) throws Exception {
return LocalDateTime.parse(arg);
}
}
I registered the adapter in package-info.java like so:
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(type=LocalDateTime.class, value=LocalDateTimeXmlAdapter.class)
})
package xml;
import java.time.LocalDateTime;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
This seems to be sufficient according to this page.
However, I keep getting the following error:
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.time.LocalDateTime does not have a no-arg default constructor.
I understand the reason for the exception being thrown, but I can hardly add a default constructor to java.time.LocalDateTime. This seems to be a shortcoming of the class / a strange design decision. Are there any workarounds?
What you have should work. One of the following may be wrong:
Since you have specified the #XmlJavaTypeAdapter at the package level it will only apply to properties on classes in your package called xml. Is there a class in your model from a different package that has a mapped property of type LocalDateTime?
It is also possible that your package-info.java file is not being compiled.
Had same behaviour: IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions.
My pbm was: I have several packages (three) where the package-info.java file is needed, like shown in the following picture.
I "solved" this pbm by adding a package-info.java in each of the three directories. Example for package fr.gouv.agriculture.dal.ct.planCharge.metier.dao.charge.xml:
#XmlJavaTypeAdapter(type = LocalDate.class, value = LocalDateXmlAdapter.class)
package fr.gouv.agriculture.dal.ct.planCharge.metier.dao.charge.xml;
If someone has a better idea than copy/paste into several package-info.java files, thanks in advance.
I have an annotation that can be added on METHOD and TYPE and is used in thousands of places in our project.
#Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
#Target({METHOD, TYPE})
#Inherited
public #interface RequiredStore{
Store value();
}
Is it possible to make the annotation deprecated only on methods while keeping it non-deprecated on types? I want other developers to be notified by IDE that it should not be used on methods any more, until we'll refactor all existing usages and finally remove the METHOD part.
If it's not possible, is there any Way to handle such case beside creating new annotation only for types and deprecating the old one?
You could use an annotation Processor.
For example, the annotation and its processor would be placed in its own .jar file and added as a dependency of the sources that use the annotation.
The custom .jar would have the following structure:
src/main/
java/com/company/annotations/
RequiredStore.java
RequiredStoreProcessor.java
resources/META-INF/services
javax.annotation.processing.Processor
RequiredStore.java stays as you have it above.
RequiredStoreProcessor.java could look something like this:
package com.company.annotations;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
#SupportedAnnotationTypes("com.company.annotations.RequiredStore")
public class RequiredStoreProcessor extends AbstractProcessor {
#Override
public boolean process(
Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (Element element
: roundEnv.getElementsAnnotatedWith(RequiredStore.class)) {
if (element.getKind().equals(ElementKind.METHOD)) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Using #RequiredStore on methods has been deprecated\n"
+ "Class: " + element.getEnclosingElement() + "\n"
+ "Method: " + element.getSimpleName() + "\n");
}
}
// Other processing...
return false;
}
#Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
The javax.annotation.processing.Processor file allows javac to pickup the Processor via SPI and simply contains
com.company.annotations.RequiredStoreProcessor
Finally, compile this into a .jar and add it to the classpath where the annotations are being used. Any methods that have the #RequiredStore will produce a compiler warning. For example, for this class,
package com.company.business;
import com.company.annotations.RequiredStore;
#RequiredStore
public interface Business {
#RequiredStore
public void someMethod();
}
The compiler warning would be this:
warning: Using #RequiredStore on methods has been deprecated
Class: com.company.business.Business
Method: someMethod
As for an indication in the IDE, you might have to write a custom inspection and unfortunately this depends on the IDE used.
Notes:
Decent custom annotations reference: Code Generation using Annotation Processors in the Java language
If you are okay about using native aspectj, another option is to use AspectJ's code enforcement policy this way:
public aspect RequiredStoreAnnotationCheck {
declare warning: execution(#RequiredStore * *.*(..)) : "Required store annotation not appropriate for methods..";
}
If the IDE is integrated with AspectJ, this would be flagged as a compile time check.
AspectJ in action book has a good amount of detail on this too.
Here is one of my blog articles for more context: http://www.java-allandsundry.com/2012/03/code-policy-enforcement-using-aspectj.html
The Situation
I have an enum class called Errors and it is in a common project. The errors enum contains an int code and a String text. The Errors enum is used in two different projects. One project is a Spring based project and the other project is in a non-Spring J2EE application.
In the Spring based project, Jackson is the library used for converting the Errors enum to Json. So if there is a request we can return an Errors enum object and it will be automatically converted to the json representation, which is {code: int, text: error} The Errors enum has the annotation:
#JsonSerialize(using=ErrorsSerializer.class)
The ErrorsSerializer class is located in the Spring based application.
In the common project, where the Errors enum is located, Gson is the library used for converting objects to its Json representation and we want to remove any references to the Jackson library.
The problem
If i remove the annotation from the Errors enum the Spring project will not return the correct representation of the Errors enum, it will only return the constant variable in quotes.
The Question
How do i remove the annotation of the Errors enum (which will remove any Jackson dependencies in the common project) and yet still have the Spring project return the correct Json representation?
Best Option So Far
The best option i have come up with so far is to create an Errors container object in the Spring application that contains the Errors enum and also has the json serialize annotation.
Thanks!
You can also specify serializer in Spring based project using Module functionality. Module allow users to customize ObjectMapper object without annotations. See below example:
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class JacksonProgram {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(Error.class, new ErrorJsonSerializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(Error.NOT_FOUND));
}
}
Above example prints:
{"code":1,"text":"Not found"}
See below links to find solution how to configure ObjectMapper in Spring app:
Register a custom Jackson ObjectMapper using Spring Javaconfig.
Configurating ObjectMapper in Spring.
I'm using CXF 2.4 with JAXB.
Could I have a global XmlAdapter for all instances of my owm class (e.g. LWDate)?
I wrote a class:
public class LWDateAdapter extends XmlAdapter<Date, LWDate>
Right now I have to add #XmlJavaTypeAdapter on each param, method or package that I plan to use with CXF. E.g.
#WebMethod void test (#WebParam(name="Birthdate") #XmlJavaTypeAdapter(LWDateAdapter.class) LWDate pBirthdate){}
I wish to ask CXF/JAXB always bind my class LWDate to java.util.Date is it possible?
UPDATE: #XmlJavaTypeAdapter works on a package level staring from version 2.4.4 according to that issue.
For your use case using the #XmlJavaTypeAdapter annotation at the package level is your best option. Below is a post where I use this strategy for the Joda-Time classes:
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html
If you have a domain class that you always want to be handled with an XmlAdapter you can use the #XmlJavaTypeAdapter annotation at the type level:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
I have downloaded a third party library and they have classes I need to refer to in the default package? How do I import these classes?
It's not possible directly with the compiler. Sun removed this capability. If something is in the default namespace, everything must be in the default namespace.
However, you can do it using the ClassLoader. Assuming the class is called Thirdparty, and it has a static method call doSomething(), you can execute it like this:
Class clazz = ClassLoader.getSystemClassLoader().loadClass("Thirdparty");
java.lang.reflect.Method method = clazz.getMethod("doSomething");
method.invoke(null);
This is tedious to say the least...
Long ago, sometime before Java 1.5, you used to be able to import Thirdparty; (a class from the unnamed/default namespace), but no longer. See this Java bug report. A bug report asking for a workaround to not being able to use classes from the default namespace suggests to use the JDK 1.3.1 compiler.
To avoid the tedious method.invoke() calls, I adapted the above solution:
Write an interface for the desired functionality in your desired my.package
package my.package;
public interface MyAdaptorInterface{
public void function1();
...
}
Write an adaptor in the default package:
public class MyAdaptor implements my.package.MyAdaptorInterface{
public void function1(){thirdparty.function1();}
...
}
Use ClassLoader/Typecast to access object from my.package
package my.package;
Class clazz = ClassLoader.getSystemClassLoader().loadClass("MyAdaptor");
MyAdaptorInterface myObj = (MyAdaptorInterface)clazz.newInstance();
myObj.function1();