While working on legacy code, I have created a wrapper class which extends com.fasterxml.jackson.databind.node.POJONode (I cannot avoid this).
Despite I have annotated the wrapper class with #JsonRootName, that is always serialized with the original class name. It looks like that the annotation is totally ignored. The same happens if I use #JsonTypeName and #JsonTypeInfo (which I found in some example around).
I have written the following simple JUnit test, which proves the issue:
import static java.lang.String.format;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import org.junit.Test;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.POJONode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class POJONodeJsonRootNameTest {
static final String XML_PRE_PTRN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%s";
private final ObjectMapper mapper = new ObjectMapper();
private final XmlMapper xmlMapper = new XmlMapper();
#Test
public void shouldConvertObjectFromXmlToJson() throws IOException {
String xml = format(XML_PRE_PTRN, "<test><name>test</name><description>test</description></test>");
JsonNode node = xmlMapper.readTree(xml);
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
String json = mapper.writeValueAsString(new TestWrapper(node));
System.out.println(json);
assertNotNull(json);
}
#JsonRootName(value = "test")
private class TestWrapper extends POJONode {
public TestWrapper(Object v) {
super(v);
}
}
}
Actual result
{"TestWrapper":{"name":"test","description":"test"}}
Expected Result
{"test":{"name":"test","description":"test"}}
In the pom file, I have added dependecies of jackson-databind, jackson-dataformat-xml and jackson-module-jaxb-annotations, all version 2.12.4 (currently the latest one).
If I am doing something wrong please suggest a fix or, possibly, an alternative. Again, let me stress out, it is necessary for me to extend POJONode class, due to legacy code.
Any help on this issue would be highly appreciated.
Thank you very much for your time and help.
As said in the comments to the question, Marco Tizzano 's code works fine up until jackson 2.10.5 version. This would suggest that after this version a regression issue had been included and appeared again in the latest jackson 2.12.4 version released in the month of July 2021. Marco Tizzano reported the issue to the developers on the FasterXml issue tracker : the link with the complete description of the issue is here.
Related
Using compile 'io.github.classgraph:classgraph:4.8.65'
https://github.com/classgraph/classgraph/wiki/ClassGraph-API
Java 8
ScanResult scanResult =
new ClassGraph().enableAllInfo()
.whitelistPackages("abc1")
.whitelistPackages("abc2")
.whitelistPackages("java")
.scan();
When I encounter ClassInfo objects for classes from the packages abc1 or abc2 they are able to reference things like java.util.HashMap, I see them in the FieldInfo.
But when I then proceed to do scanResult.getClassInfo("java.util.HashMap"), it returns null.
(following FieldInfos for other classes within the abc1 or abc2 packages do return more ClassInfo objects)
My question is, is it correct to think I would be able to get the ClassInfo objects to the java jre classes via the ClassGraph method chaining as shown above?
Added this test which fails, it surprisingly only prints one class rather than expected dozens:
package abc;
import io.github.classgraph.ScanResult;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import java.util.*;
import java.io.*;
import java.util.function.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
#SpringJUnitConfig
#SpringBootTest(classes = {})
public class ExamplesSpec {
#org.junit.jupiter.api.Test
#org.junit.jupiter.api.DisplayName(value="test_for_built_in_java_jre_classes")
public void test_on_line_42() throws Exception {
System.out.println("test_for_built_in_java_jre_classes");
ClassInfo found = null;
try (
ScanResult result = new ClassGraph().enableAllInfo().whitelistPackages("java.util").scan()
) {
System.out.println("here all the classes....");
for( ClassInfo item : result.getAllClasses()) {
System.out.println("here classinfo: " + item);
}
found = result.getClassInfo("java.util.HashMap");
}
assert found != null;
}
}
The only class found is this:
here classinfo: public class java.util.zip.VFSZipFile implements java.util.zip.ZipConstants
Found the answer!
In the setup of the ClassGraph, in order to scan the jre provided classes, you would need to add this to the method chaining:
.enableSystemJarsAndModules()
For example:
new ClassGraph().enableAllInfo()
.whitelistPackages("abc1")
.whitelistPackages("abc2")
.whitelistPackages("java")
.enableSystemJarsAndModules()
.scan();
This is detailed in the documentation found here:
https://github.com/classgraph/classgraph/wiki/API:-ClassGraph-Constructor#configuring-the-classgraph-instance
I configured jackson so that it gives me a smiple string representation if java.time.LocalDate and java.time.LocalDateTime. This works find in the serialization process, e.g when I get data on the REST api.
It doesn't work the other way round though. When I try to send data to the server and the JSON should be parsed to java objects this exception is thrown:
javax.ws.rs.client.ResponseProcessingException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDateTime: no String-argument constructor/factory method to deserialize from String value ('2018-04-19T14:10:30.903')
After a few hours of research I managed to get it to work, but only with the attributes I annotated with #JsonDeserialize(using = LocalDateDeserializer.class) or #JsonDeserialize(using = LocalDateTimeDeserializer.class) respectively.
In my opinion it would be ideal, if I could define these mappings in one central place.
ObjectMapper configuration:
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
#Override
public ObjectMapper getContext(Class<?> aClass) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
I tried to add custom deserializers to the JavaTimeModule, but without success:
JavaTimeModule dateTimeModule = new JavaTimeModule();
dateTimeModule.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
dateTimeModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
mapper.registerModule(dateTimeModule);
Long story short: Is there a way to define the mapping globally, so that I do not need these annotations on every field. Thanks!
EDIT:
Alright: I tested it with postman and without the annotations and it worked as expected. However, when I run the unit test (JerseyTest) it throws the mentioned exception. I register the ObjectMapperContextResolver to the test application, but probably I am missing something.
Sorry about not mentioning that I was in the unit tests.
TestClass:
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import javax.ws.rs.core.Application;
import java.time.LocalDate;
import java.time.LocalDateTime;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class PocRestTest extends JerseyTest {
private static PocService mockedPocService;
#Override
protected Application configure() {
mockedPocService = Mockito.mock(PocService.class);
ResourceConfig config = new ResourceConfig();
config.register(new PocRest(mockedPocService));
config.register(ObjectMapperContextResolver.class);
return config;
}
private Poc dto;
#Before
public void init() {
dto = new Poc();
dto.setId(1);
dto.setName("hi rest");
dto.setDate(LocalDate.now());
dto.setDateTime(LocalDateTime.now());
doReturn(dto).when(mockedPocService).getPocById(1);
}
#Test
public void test() {
Poc response = target("poc/1").request().get(Poc.class);
assertEquals(dto.getId(), response.getId());
assertEquals(dto.getName(), response.getName());
assertEquals(dto.getDate(), response.getDate());
assertEquals(dto.getDateTime(), response.getDateTime());
verify(mockedPocService).getPocById(1);
verifyNoMoreInteractions(mockedPocService);
}
}
You registered the ContextResolver with the server via the configure() method of the JerseyTest, but if you look at the exception, you'll see that is a client side exception (notice the client in the package name). So the problem is on the client side. What you are missing is that the deserialization also needs to happen on the client side from JSON to Poc, so you also need the ContextResolver registered on the client. To do that, you can override configureClient() on the JerseyTest and register the ContextResolver there.
#Override
public void configureClient(ClientConfig config) {
config.register(ObjectMapperContextResolver.class);
}
In my code I am using following annotation several times:
#JsonSerialize(using = classOf[CustomColorRGBASerializer])
To keep my code short and DRY, I would like to create a shortcut to this, something like:
class JsonSerializeARGB
extends #JsonSerialize(using = classOf[CustomColorRGBASerializer])
which I could then use as a new #JsonSerializeARGB annotation
I can use annotation, but I do not know how to define them, therefore my attempt certainly looks naive and obviously incorrect, but I hope it bears the meaning through.
I have read How do you define an #interface in Scala? and How to create annotations and get them in scala, but they did not help me much, as I do not want to create a brand new annotation, rather "subclass" existing annotation. Can this be done?
If there is no Scala solution, can something like this be done in Java? (The Jackson annotations I am working with are defined in Java anyway).
I'm afraid there is no way to subtype annotation with Java (and Scala) language mechanisms. I think that the only solution is to make a Scala macro with the annotation.
Macro annotations are available with Macro Paradise plugin for Scala compiler. Hopefully they 'll be included in Scala 2.13. To configure SBT for Macro Paradise you may want to follow this question. There is also a useful example of project making use of macro paradise.
I believe that this can be done better (especially DefDef matching), but macro similar to this one should solve your problem:
import scala.reflect.macros._
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
class JsonSerializeARGB extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro JsonSerializeARGBMacroImpl.impl
}
object JsonSerializeARGBMacroImpl extends JsonSerializeARGBMacro
class JsonSerializeARGBMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def modifiedDef(d: DefDef) = {
val (mods, name, tparams, paramss, tpt, body) = try {
val q"$mods def $name[..$tparams](...$paramss): $tpt = $body" = d
(mods, name, tparams, paramss, tpt, body)
} catch {
case _: MatchError => c.abort(c.enclosingPosition, "Failed to match...")
}
//TODO there is a problem with modifiers
c.Expr(q"""
#JsonSerialize(using = classOf[CustomColorRGBASerializer])
def $name[..$tparams](...$paramss): $tpt = $body
""")
}
annottees.map(_.tree) match {
case (d: DefDef) :: Nil => modifiedDef(d)
case _ => c.abort(c.enclosingPosition, "Invalid annottee.")
}
}
}
Looking at Java, there is no reasonable way to do this. Annotations cannot be extended in current Java versions, so the easiest approach fails. An other possiblity would be to use reflection to replace all occurrences of a JsonSerializeARGB with JsonSerialize, though this would only work at runtime, not at compile time. Yet the Java Reflection API only supports reading annotations, not adding them.
So there are two theoretical approaches:
Messing with the compiled byte code, but nobody can honestly want to do that.
Modifying Jackson (or any other library that reads the annotations) to recognize your custom JsonSerializeARGB annotation.
I’m not familiar with Scala, so I do not know whether there are other options available there. But I doubt that Scala provides methods to add or extends annotation that Java doesn’t.
Taking a different approach. Jackson supports programattically defining serializers. So you can define your own annotation and then use reflection to find all classes with your annotation and add the serializer mapping.
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("MyModule", new Version(1, 0, 0, null))
// use reflections to find all classes with Annotation the
for (classWithAnnotation <- classesWithAnnotation) {
simpleModule.addSerializer(classWithAnnotation, new CustomColorRGBASerializer());
}
mapper.registerModule(simpleModule);
Here is the example I tried to get what you wanted to do with fasterXML library:
1. Create your own CustomSerializer
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.core.JsonProcessingException;
public class CustomSerializer extends JsonSerializer<CustomDTO> {
#Override
public void serialize(CustomDTO value, JsonGenerator gen,
com.fasterxml.jackson.databind.SerializerProvider serializers)
throws IOException,
JsonProcessingException {
gen.writeStartObject();
gen.writeStringField("AccentColor", value.getAccentColor());
gen.writeStringField("ButtonColor", value.getButtonColor());
gen.writeEndObject();
}
}
2. Create Annotation to use this CustomSerializer:
As of Scala 2.11 this needs to be done in Java, as in Scala it is currently not possible to define annotations with runtime retention.
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonSerialize(using = CustomSerializer.class)
public #interface JsonSeriliazerCustom {}
3. Use this on CustomDTO or your class as follows:
#JsonSeriliazerCustom
public class CustomDTO {
private String buttonColor;
private String accentColor;
private String frontColor;
public String getButtonColor() {
return buttonColor;
}
public void setButtonColor(String buttonColor) {
this.buttonColor = buttonColor;
}
public String getAccentColor() {
return accentColor;
}
public void setAccentColor(String accentColor) {
this.accentColor = accentColor;
}
public String getFrontColor() {
return frontColor;
}
public void setFrontColor(String frontColor) {
this.frontColor = frontColor;
}
}
4. Write your main method like this:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.opera.oss.core.dto.CustomDTO;
public class TestJson {
public static void main(String[] args)
{
CustomDTO responseDTO = new CustomDTO();
responseDTO.setAccentColor("red");
responseDTO.setButtonColor("blue");
responseDTO.setFrontColor("yellow");
System.out.println("hey");
ObjectMapper om = new ObjectMapper();
VisibilityChecker<?> checker = om.getSerializationConfig().getDefaultVisibilityChecker();
om.setVisibilityChecker(checker.withFieldVisibility(JsonAutoDetect.Visibility.ANY));
try {
System.out.println(om.writer().writeValueAsString(responseDTO));
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Libraries used: fasterXML - 2.5.0 version - jackson-core, jackson-data-bind and jackson-annotations
Hi i am new in java reflection domain.So can anyone guide me in this problem scenario.
I have a class named "SomClass.java" and it imports a package named "SomPackage.RefClass" And some other java libraries like java.lang.. etc.
Now i wish to get know all the imports defined in a class through reflection.
import SomPackage.RefClass;
import java.lang.reflect.Field;
import java.io.IOException;
public class SomeClass{
RefClass refClass_Obj;
String nationality;
///some other members
}
I just wanna know the list of all import defined in a class using reflection.
I have seen a Question posted hear similar to my Q but it is not well elaborated so,need some good direction of help.
thanks in advance.
I just wanna know the list of all
import defined in a class using
reflection
You can't because the compiler doesn't put them into the object file. It throws them away. Import is just a shorthand to the compiler.
Imports are a compile-time feature - there's no difference to the compiled code between a version which uses the full name of the type everywhere it's mentioned, a version which imports everything using a *, and a version which imports classes by full name.
If you want to find all the types used within the compiled code, that's a slightly different matter. You may want to look at BCEL as a way of analyzing bytecode.
I think you can use Qdox to get all the imports in a class which is not actually through reflection, but it can serve your purpose :
String fileFullPath = "Your\\java\\ file \\full\\path";
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSource(new FileReader( fileFullPath ));
JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();
for ( String imp : imports )
{
System.out.println(imp);
}
As suggested by #Asraful Haque qdox helps to read imports of java file
Use Maven dependency
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>2.0.1</version>
</dependency>
Please refer modified version of code
package readimports;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Collection;
import java.util.List;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaSource;
public class TestReadAllImport {
public static void main(String[] args) throws FileNotFoundException {
String fileFullPath = "path to java file";
JavaProjectBuilder builder = new JavaProjectBuilder();
builder.addSource(new FileReader( fileFullPath ));
Collection<JavaSource> srcs = builder.getSources();
for(JavaSource src : srcs) {
List<String> imports = src.getImports();
for ( String imp : imports )
{
System.out.println(imp);
}
}
}
}
Thanks for sharing the qdox, I used it to recursively find all imports and find unique packages and unique imports.
<!-- https://mvnrepository.com/artifact/com.thoughtworks.qdox/qdox -->
<dependency>
<groupId>com.thoughtworks.qdox</groupId>
<artifactId>qdox</artifactId>
<version>2.0.3</version>
</dependency>
Using simple recursion to get all packages and imports of the given class.
package test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaSource;
public class ImportsIdentifier {
private static String sysPath ="//<Absolute Path>/src/main/java/";
private static String fileType = ".java";
private static Set<String> importFiles = new HashSet<>();
private static Set<String> packages = new HashSet<>();
public static void main(String[] args) throws FileNotFoundException {
String path = sysPath + "<java file path>";
printImports(path);
System.out.println(importFiles);
System.out.println(packages);
}
private static void printImports(String path) throws FileNotFoundException {
JavaProjectBuilder jp = new JavaProjectBuilder();
jp.addSource(new FileReader(path));
Collection<JavaSource> srcs = jp.getSources();
for (JavaSource src : srcs) {
System.out.println(src.getPackage());
packages.add(src.getPackage().toString());
for(String imprt: src.getImports()) {
if(imprt.startsWith("<filter for any package>")) {
imprt = sysPath+imprt.replaceAll("\\.", "/")+fileType;
if(importFiles.contains(imprt)) {
continue;
}
importFiles.add(imprt);
System.out.println(imprt);
printImports(imprt);
}
}
}
}
}
I am trying to get JAXB to work with a groovy class of mine, however, it appears it doesn't work but the java version does. Here is the code...
Here are the Scenarios:
If 2 and 3 are uncommented it works fine.
If 1 and 4 are uncommented I get:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException:
2 counts of IllegalAnnotationExceptions
groovy.lang.MetaClass is an interface, and JAXB can't handle interfaces.
If 1 and 5 are uncommented I get:
javax.xml.bind.JAXBException: class org.oclc.presentations.simplejaxb.PlayerGroovy
nor any of its super class is known to this context.
Any ideas?
Java:
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Player {
}
Groovy:
import javax.xml.bind.annotation.XmlRootElement
#XmlRootElement
public class PlayerGroovy {
}
Test:
import org.junit.Test
import javax.xml.bind.JAXBContext
import javax.xml.bind.Marshaller
import org.junit.Assert
class PlayerTest {
#Test
public void testJaXB(){
//1 PlayerGroovy player = new PlayerGroovy()
//2 Player player = new Player()
StringWriter writer = new StringWriter();
//3 JAXBContext context = JAXBContext.newInstance(Player.class);
//4 JAXBContext context = JAXBContext.newInstance(PlayerGroovy.class);
//5 JAXBContext context = JAXBContext.newInstance(PlayerGroovy.getClass());
Marshaller m = context.createMarshaller();
m.marshal(player, writer);
println(writer)
Assert.assertTrue(true)
}
}
Uncommenting 1 and 4 is the correct way to set JAXB up with Groovy. The reason it is not working is that each Groovy Class has a metaClass property on it. JAXB is trying to expose this as a JAXB property which obviously fails. Since you don't declare the metaClass property yourself, it is not possible to annotate it to have JAXB ignore it. Instead you and set the XmlAccessType to NONE. This disable's JAXB's auto-discovery of properties to expose as XML elements. After you do that, you need to explicitly declare any fields you want exposed.
Example:
#XmlAccessorType( XmlAccessType.NONE )
#XmlRootElement
public class PlayerGroovy {
#XmlAttribute
String value
}
I was having the same problem while exposing a Grails GORM object. After researching the solution posted above, using #XmlAccessorType( XmlAccessType.NONE ), I quickly grew tired of marking everything as #XmlAttribute.
I'm having plenty of success using:
#XmlAccessorType( XmlAccessType.FIELD )
#XmlRootElement
public class PlayerGroovy {
String value
}
See: XmlAccessType
Thanks to the original answer for getting me started in the right direction.
The solution does not seem to work on an abstract subclass. I think it's because the compiler does not generate the getMetaClass override code. I ended up mimicking the steps from this question as follows:
#XmlAccessorType(XmlAccessType.NONE)
package groovy.lang;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
Yes, it's kinda weird. In my case, I have code like this:
package pkg;
abstract class ScriptMomma extends groovy.lang.Script {
// write some nice supporting code here
}
And to execute my scripts, I have:
def config = new org.codehaus.groovy.control.CompilerConfiguration()
config.scriptBaseClass = 'pkg.ScriptMomma'
ScriptMomma mine = new GroovyShell(config).evaluate(scriptFile, 'TheOne')
I'd prefer a better solution, but right now this is all I have.