How do I get Groovy and JAXB to play nice together - java

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.

Related

`#JsonRootName` ignored when used with POJONode

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.

Is this a valid use of the java ClassGraph api?

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

How to create a shortcut to existing annotation?

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

How can I prevent Eclipse from supplying deprecated classes when importing?

I have a recurring problem using Eclipse. Consider the following example:
As you can see I've pressed Ctrl+Shift+O. I can choose from a deprecated and a non-deprecated annotation. My problem is that I am often supplied with dozens of classes and half of them are deprecated (a perfect example is the JUnit Assert classes).
My question is how can I make Eclipse ignore all deprecated classes when organizing imports?
Currently Eclipse does not provide such an option... Eclipse Documentation for Organise Imports (Kepler version).
However, with a fudge you can achieve the same result...
Eclipse allows you to provide a list of classes/packages to filter-out.
To do this, navigate to Preferences > Type Filters.
I've done this in my environment to ensure "java.awt.List" is not suggested when I really want "java.util.List".
What you want is to add all deprecated classes to this list.
This list is maintained in your eclipse workspace preferences...
File ... C:\Users\[YOUR_USER_NAME]\workspace\.metadata\.plugins\org.eclipse.core.runtime\.settings\org.eclipse.jdt.ui.prefs
Property ... org.eclipse.jdt.ui.typefilter.enabled=java.awt.List;
All that is required is that you create a list of deprecated classes, and store it in this properties file.
Eclispe can help create this list...
Perform a "Java Search" for "Deprecated".
Then group the results by type.
And copy the results using "Copy Qualified Name"
The results will contain Generics, and this should be removed.
For example, "javafx.scene.control.Cell<T>" should read "javafx.scene.control.Cell".
In addition to containing deprecated classes, the results will also contain any class that has the word "Deprecated". This could be a comment or a method annotation. This list will need to be filtered to retain only deprecated classes.
The script below processes this class list to remove generics, and filtering out classes that are not deprecated (ie, only has method deprecation). The class list is read from a file named "DeprecatedClassList.txt". When it cannot check the class annotation, it skips the class and prints it out (for manual checking).
import java.lang.annotation.Annotation;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ConfigurationGenerator {
public static void main(String[] args) throws Exception {
List<String> cleanedList = Files
.readAllLines(Paths.get("DeprecatedClassList.txt")).stream()
.map(ConfigurationGenerator::removeGenerics)
.filter(ConfigurationGenerator::hasDeprecatedConstructor)
.collect(Collectors.toList());
String propertyName = "org.eclipse.jdt.ui.typefilter.enabled=";
String propertyValue = String.join(";", cleanedList).concat(";");
String configuration = propertyName + propertyValue;
System.out.println("Configuration property...");
System.out.println(configuration);
}
public static String removeGenerics(String className) {
int openingBracket = className.indexOf("<");
if (openingBracket == -1)
return className;
else
return className.substring(0, openingBracket);
}
public static boolean hasDeprecatedConstructor(String className) {
Class theClass = null;
try {
theClass = Class.forName(className);
} catch (Throwable e) {
// Ignore bad results
System.out.println("Skipping: " + className);
return false;
}
Annotation[] annotations = theClass.getAnnotations();
Optional<Annotation> deprecatedConstructor = Stream
.of(annotations)
.filter(annotation -> annotation.toString().equals(
"#java.lang.Deprecated()")).findAny();
return deprecatedConstructor.isPresent();
}
}
There is one problem with this approach though. You may want to use a deprecated class when a non-deprecated version does not exist. You will not see the deprecated class if it has been purposefully hidden. To resolve that, just be sure you exclude them from the filter.

JAXB inheritance, unmarshal to subclass of marshaled class

I'm using JAXB to read and write XML. What I want is to use a base JAXB class for marshalling and an inherited JAXB class for unmarshalling. This is to allow a sender Java application to send XML to another receiver Java application. The sender and receiver will share a common JAXB library. I want the receiver to unmarshall the XML into a receiver specific JAXB class which extends the generic JAXB class.
Example:
This is the common JAXB class which is used by the sender.
#XmlRootElement(name="person")
public class Person {
public String name;
public int age;
}
This is the receiver specific JAXB class used when unmarshalling the XML. The receiver class has logic specific to the receiver application.
#XmlRootElement(name="person")
public class ReceiverPerson extends Person {
public doReceiverSpecificStuff() ...
}
Marshalling works as expected. The problem is with unmarshalling, it still unmarshals to Person despite the JAXBContext using the package name of the subclassed ReceiverPerson.
JAXBContext jaxbContext = JAXBContext.newInstance(package name of ReceiverPerson);
What I want is to unmarshall to ReceiverPerson. The only way I've been able to do this is to remove #XmlRootElement from Person. Unfortunately doing this prevents Person from being marshaled. It's as if JAXB starts at the base class and works its way down until it finds the first #XmlRootElement with the appropriate name. I've tried adding a createPerson() method which returns ReceiverPerson to ObjectFactory but that doesn't help.
The following snippet is a method of a Junit 4 test with a green light:
#Test
public void testUnmarshallFromParentToChild() throws JAXBException {
Person person = new Person();
int age = 30;
String name = "Foo";
person.name = name;
person.age= age;
// Marshalling
JAXBContext context = JAXBContext.newInstance(person.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(person, writer);
String outString = writer.toString();
assertTrue(outString.contains("</person"));
// Unmarshalling
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(outString);
RecieverPerson reciever = (RecieverPerson)unmarshaller.unmarshal(reader);
assertEquals(name, reciever.name);
assertEquals(age, reciever.age);
}
The important part is the use of the JAXBContext.newInstance(Class... classesToBeBound) method for the unmarshalling context:
context = JAXBContext.newInstance(Person.class, RecieverPerson.class);
With this call, JAXB will compute a reference closure on the classes specified and will recognize RecieverPerson. The test passes. And if you change the parameters order, you'll get a java.lang.ClassCastException (so they must be passed in this order).
You're using JAXB 2.0 right? (since JDK6)
There is a class:
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
which one can subclass, and override following methods:
public abstract BoundType unmarshal(ValueType v) throws Exception;
public abstract ValueType marshal(BoundType v) throws Exception;
Example:
public class YourNiceAdapter
extends XmlAdapter<ReceiverPerson,Person>{
#Override public Person unmarshal(ReceiverPerson v){
return v;
}
#Override public ReceiverPerson marshal(Person v){
return new ReceiverPerson(v); // you must provide such c-tor
}
}
Usage is done by as following:
#Your_favorite_JAXB_Annotations_Go_Here
class SomeClass{
#XmlJavaTypeAdapter(YourNiceAdapter.class)
Person hello; // field to unmarshal
}
I'm pretty sure, by using this concept you can control the marshalling/unmarshalling process by yourself (including the choice the correct [sub|super]type to construct).
Subclass Person twice, once for receiver and once for sender, and only put the XmlRootElement on these subclassses (leaving the superclass, Person, without an XmlRootElement). Note that sender and receiver both share the same JAXB base classes.
#XmlRootElement(name="person")
public class ReceiverPerson extends Person {
// receiver specific code
}
#XmlRootElement(name="person")
public class SenderPerson extends Person {
// sender specific code (if any)
}
// note: no #XmlRootElement here
public class Person {
// data model + jaxb annotations here
}
[tested and confirmed to work with JAXB]. It circumvents the problem you note, when multiple classes in the inheritance hierarchy have the XmlRootElement annotation.
This is arguably also a neater and more OO approach, because it separates out the common data model, so it's not a "workaround" at all.
Create a custom ObjectFactory to instantiate the desired class during unmarshalling. Example:
JAXBContext context = JAXBContext.newInstance("com.whatever.mypackage");
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.internal.bind.ObjectFactory", new ReceiverPersonObjectFactory());
return unmarshaller;
public class ReceiverPersonObjectFactory extends ObjectFactory {
public Person createPerson() {
return new ReceiverPerson();
}
}
I am not sure why you would want to do this... it doesn't seem all that safe to me.
Consider what would happen in ReceiverPerson has additional instance variables... then you would wind up with (I guess) those variables being null, 0, or false... and what if null is not allowed or the number must be greater than 0?
I think what you probably want to do is read in the Person and then construct a new ReceiverPerson from that (probably provide a constructor that takes a Person).
Since you really have two separate apps, compile them with different versions of the class "Person" - with the receiver app not having #XmlRootElement(name="person") on Person. Not only is this ugly, but it defeats the maintainability you wanted from using the same definition of Person for both sender and receiver. Its one redeeming feature is that it works.

Categories

Resources