JAXB marshalling: treat empty object like it's null - java

I want to explain my issue with a simple example:
Foo:
#SomeXMLAnnotations
public class Foo {
// Bar is just a random class with its own XML annotations
#XmlElement(required = true)
Bar someBarObj;
boolean chosen = true;
boolean required = true;
public Foo(){
chosen = false;
}
public Foo(Bar someBarObj){
this.someBarObj = someBarObj;
}
}
MyClass:
#SomeXMLAnnotations
public class MyClass {
#XmlElement(required = false)
Foo anyFooObj;
#XmlElement(required = true)
Foo anyFooObjRequired;
public MyClass (){ }
public MyClass (Foo anyFooObj, Foo anyFooObjRequired){
this.anyFooObj = anyFooObj;
if(anyFooObj == null)
this.anyFooObj = new Foo();
/*
* This is the reason why i can't let 'anyFooObj' be 'null'.
* So 'anyFooObj' MUST be initialized somehow.
* It's needed for some internal logic, not JAXB.
*/
anyFooObj.required = false;
this.anyFooObjRequired = anyFooObjRequired;
}
}
Example Objects:
Foo fooRequired = new Foo(new Bar());
MyClass myObj = new MyClass(null, fooRequired);
When i try to marshal myObj now, it throws an exception like this:
org.eclipse.persistence.oxm.record.ValidatingMarshalRecord$MarshalSAXParseException;
cvc-complex-type.2.4.b: The content of element 'n0:anyFooObj ' is not complete.
One of '{"AnyNamespace":someBarObj}' is expected.
This happens because anyFooObj is initialized but it's required, member
someBarObj isn't.
Possible Solution:
I know i could add this method to MyClass:
void beforeMarshal(Marshaller m){
if(! anyFooObj.chosen)
anyFooObj= null;
}
}
But I have a lot of classes and those classes have a lot of not required fields.
So this solution would take ages and doesn't look like a proper solution as well.
My Question:
Is there a way to tell JAXB that it should treat empty objects like they were null? Or that it should ignore an element when it's not properly set. Something like this for example:
#XmlElement(required = false, ingnoreWhenNotMarshallable = true)
Foo anyFooObj;
NOTE:
I'm NOT the developer of the code. I just have to add JAXB to the project and make everything compatible with a given XSD file. I'm NOT allowed to change the relation between classes.

I think you're trying to make the JAXB marshaller do something it's really not designed to do, so I'd say you're into hack territory here. I'd recommend pushing back on the requirements to try and avoid having this problem in the first place.
That said, if you have to do it then given your requirement to avoid writing code for each class/field, I think you'll want to use reflection for this - I've included an example below that reflectively inspects the values of all fields.
Useful extensions would be:
Have it consider getter methods too
Make the null-setting behaviour opt-in by requiring the field has an additional annotation - you could name it #JAXBNullIfEmpty
Example.java:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringWriter;
import java.lang.reflect.Field;
public class Example
{
public abstract static class JAXBAutoNullifierForEmptyOptionalFields
{
void beforeMarshal(Marshaller x)
{
try
{
for (Field field : this.getClass().getFields())
{
final XmlElement el = field.getAnnotation(XmlElement.class);
// If this is an optional field, it has a value & it has no fields populated then we should replace it with null
if (!el.required())
{
if (JAXBAutoNullifierForEmptyOptionalFields.class.isAssignableFrom(field.getType()))
{
final JAXBAutoNullifierForEmptyOptionalFields val = (JAXBAutoNullifierForEmptyOptionalFields) field.get(
this);
if (val != null && !val.hasAnyElementFieldsPopulated())
field.set(this, null); // No fields populated, replace with null
}
}
}
}
catch (IllegalAccessException e)
{
throw new RuntimeException("Error determining if class has all required fields: " + this, e);
}
}
boolean hasAnyElementFieldsPopulated()
{
for (Field field : this.getClass().getFields())
{
try
{
if (field.isAnnotationPresent(XmlElement.class))
{
// Retrieve value
final Object val = field.get(this);
// If the value is non-null then at least one field has been populated
if (val != null)
{
return true;
}
}
}
catch (IllegalAccessException e)
{
throw new RuntimeException("Error determining if class has any populated JAXB fields: " + this, e);
}
}
// There were no fields with a non-null value
return false;
}
}
#XmlRootElement
public static class MyJAXBType extends JAXBAutoNullifierForEmptyOptionalFields
{
#XmlElement
public String someField;
#XmlElement
public MyJAXBType someOtherField;
public MyJAXBType()
{
}
public MyJAXBType(final String someField, MyJAXBType someOtherField)
{
this.someField = someField;
this.someOtherField = someOtherField;
}
}
public static void main(String[] args) throws Exception
{
final Marshaller marshaller = JAXBContext.newInstance(MyJAXBType.class).createMarshaller();
MyJAXBType innerValue = new MyJAXBType(); // Unpopulated inner value
MyJAXBType value = new MyJAXBType("some text value", innerValue);
final StringWriter sw = new StringWriter();
marshaller.marshal(value, sw); // Omits "someOtherField"
System.out.println(sw.toString());
}
}

Related

Copy object properties by direct field access

Is there an easy way to copy an object's property's onto another object of a different class which has the same field names using direct field access - i.e. when one of the classes does not have getters or setters for the fields? I can use org.springframework.beans.BeanUtils#copyProperties(Object source, Object target) when they both have getter and setter methods, but what can I do when they don't?
It may also be relevant that the fields are public.
I know that I can write my own code to do this using reflection, but I'm hoping that there's some library that provides a one-liner.
I didn't find a 3rd-party library to do this quite how I wanted. I'll paste my code here in case it is useful to anyone:
import java.lang.reflect.Field;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An alternative to Spring's BeanUtils#copyProperties for classes that don't have getters and setters.
*/
public class FieldCopier {
private static final Logger log = LoggerFactory.getLogger(FieldCopier.class);
/** Always use the same instance, so that we can cache the fields. */
private static final FieldCopier instance = new FieldCopier();
/** Caching the paired fields cuts the time taken by about 25% */
private final Map<Map.Entry<Class<?>, Class<?>>, Map<Field, Field>> PAIRED_FIELDS = new ConcurrentHashMap<>();
/** Caching the fields cuts the time taken by about 50% */
private final Map<Class<?>, Field[]> FIELDS = new ConcurrentHashMap<>();
public static FieldCopier instance() {
return instance;
}
private FieldCopier() {
// do not instantiate
}
public <S, T> T copyFields(S source, T target) {
Map<Field, Field> pairedFields = getPairedFields(source, target);
for (Field sourceField : pairedFields.keySet()) {
Field targetField = pairedFields.get(sourceField);
try {
Object value = getValue(source, sourceField);
setValue(target, targetField, value);
} catch(Throwable t) {
throw new RuntimeException("Failed to copy field value", t);
}
}
return target;
}
private <S, T> Map<Field, Field> getPairedFields(S source, T target) {
Class<?> sourceClass = source.getClass();
Class<?> targetClass = target.getClass();
Map.Entry<Class<?>, Class<?>> sourceToTarget = new AbstractMap.SimpleImmutableEntry<>(sourceClass, targetClass);
PAIRED_FIELDS.computeIfAbsent(sourceToTarget, st -> mapSourceFieldsToTargetFields(sourceClass, targetClass));
Map<Field, Field> pairedFields = PAIRED_FIELDS.get(sourceToTarget);
return pairedFields;
}
private Map<Field, Field> mapSourceFieldsToTargetFields(Class<?> sourceClass, Class<?> targetClass) {
Map<Field, Field> sourceFieldsToTargetFields = new HashMap<>();
Field[] sourceFields = getDeclaredFields(sourceClass);
Field[] targetFields = getDeclaredFields(targetClass);
for (Field sourceField : sourceFields) {
if (sourceField.getName().equals("serialVersionUID")) {
continue;
}
Field targetField = findCorrespondingField(targetFields, sourceField);
if (targetField == null) {
log.warn("No target field found for " + sourceField.getName());
continue;
}
if (Modifier.isFinal(targetField.getModifiers())) {
log.warn("The target field " + targetField.getName() + " is final, and so cannot be written to");
continue;
}
sourceFieldsToTargetFields.put(sourceField, targetField);
}
return Collections.unmodifiableMap(sourceFieldsToTargetFields);
}
private Field[] getDeclaredFields(Class<?> clazz) {
FIELDS.computeIfAbsent(clazz, Class::getDeclaredFields);
return FIELDS.get(clazz);
}
private <S> Object getValue(S source, Field sourceField) throws IllegalArgumentException, IllegalAccessException {
sourceField.setAccessible(true);
return sourceField.get(source);
}
private <T> void setValue(T target, Field targetField, Object value) throws IllegalArgumentException, IllegalAccessException {
targetField.setAccessible(true);
targetField.set(target, value);
}
private Field findCorrespondingField(Field[] targetFields, Field sourceField) {
for (Field targetField : targetFields) {
if (sourceField.getName().equals(targetField.getName())) {
if (sourceField.getType().equals(targetField.getType())) {
return targetField;
} else {
log.warn("Different types for field " + sourceField.getName()
+ " source " + sourceField.getType() + " and target " + targetField.getType());
return null;
}
}
}
return null;
}
}
Write a simple utility class for that and you got your one liner... this task is IMHO to easy to use a library for it.
Just keep in mind to make your fields accessible if they aren't by default. Here are two functions you could adapt from our codebase:
public void injectIntoObject(Object o, Object value) {
try {
getField().set(o, value);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Illegal argument while injecting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'. Got one of type "+value.getClass().getCanonicalName()+" but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e) {
getField().setAccessible(true);
try {
getField().set(o, value);
} catch (IllegalArgumentException e1) {
throw new RuntimeException("Illegal argument while injecting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'. Got one of type "+value.getClass().getCanonicalName()+" but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e1) {
throw new RuntimeException("Access exception while injecting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'!",e);
}
} catch (Exception e) {
throw new RuntimeException("Exception while setting property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' to '"+value+"'!",e);
}
}
public Object extractFromObject(Object o) {
try {
return getField().get(o);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Illegal argument while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e) {
getField().setAccessible(true);
try {
return getField().get(o);
} catch (IllegalArgumentException e1) {
throw new RuntimeException("Illegal argument while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"' but needed one of "+type.getCanonicalName()+"!",e);
} catch (IllegalAccessException e1) {
throw new RuntimeException("Access exception while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"'!",e);
}
} catch (Exception e) {
throw new RuntimeException("Exception while read property '"+name+"' of class '"+beanDef.getName()+"' in object '"+o+"'!",e);
}
}
getField() returns a java.lang.Field, should be easy to implement.
I would strongly suggest that you avoid using reflection for this, as it leads to code that is difficult to understand and maintain. (Reflection is ok for testing and when creating frameworks, other than this it probably creates more problems than it solves.)
Also, if a property of an object needs to be accessed by something other than the object, it needs a scope that is not private (or an accessor/getter that is not private). That is the whole point of variable scopes. Keeping a variable private without accessors, and then using it anyways through reflection is just wrong, and will just lead to problems, as you are creating code that lies to the reader.
public class MyClass {
private Integer someInt;
private String someString;
private List<Double> someList;
//...
}
public class MyOtherClass {
private Integer someInt;
private String someString;
private List<Double> someList;
private boolean somethingElse;
public copyPropertiesFromMyClass(final MyClass myClass) {
this.someInt = myClass.getSomeInt();
this.someString = myClass.getSomeString();
this.someList = new ArrayList<>(myClass.getSomeList());
}
}

How to change value of annotation of field on runtime in Java?

I need to change (or remove whole) annotation value on runtime from one class. I got the exapmles from SO but this solution works only for class annotations, not for the field annotations. Any idea how to get this done? Thre reason for this is to make minor change in DB model definition classes to not use enum fields as inmemory db's not having this data type like MySQL does.
Here is working(partially) solution found on SO:
package annotations;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;
public class AnnotationModification2 {
public static void main(String[] args) throws Exception {
Something annotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + Foobar.class.getAnnotation(Something.class).someProperty());
changeAnnotationValue(annotation, "someProperty", "another value");
System.out.println("modifiedAnnotation = " + Foobar.class.getAnnotation(Something.class).someProperty());
annotation = (Something) Foobar.class.getDeclaredField("name").getAnnotations()[0];
System.out.println("oldAnnotation = " + annotation.someProperty());
changeAnnotationValue(annotation, "someProperty", "another value");
System.out.println("modifiedAnnotation = " + annotation.someProperty());
System.out.println(Foobar.class.getDeclaredField("name").getAnnotation(Something.class).someProperty());
}
/**
* Changes the annotation value for the given key of the given annotation to newValue and returns
* the previous value.
*/
#SuppressWarnings("unchecked")
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
Object handler = Proxy.getInvocationHandler(annotation);
Field f;
try {
f = handler.getClass().getDeclaredField("memberValues");
} catch (NoSuchFieldException | SecurityException e) {
throw new IllegalStateException(e);
}
f.setAccessible(true);
Map<String, Object> memberValues;
try {
memberValues = (Map<String, Object>) f.get(handler);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
Object oldValue = memberValues.get(key);
if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
throw new IllegalArgumentException();
}
memberValues.put(key,newValue);
return oldValue;
}
#Something(someProperty = "some value")
public static class Foobar {
#Something(someProperty = "Old field value!")
private String name;
}
#Retention(RetentionPolicy.RUNTIME)
#interface Something {
String someProperty();
}
}
Try to use "getField" method:
Something annotation = (Something) Foobar.class.getField("name").getAnnotations()[0];

Annotating the functional interface of a Lambda Expression

Java 8 introduces both Lambda Expressions and Type Annotations.
With type annotations, it is possible to define Java annotations like the following:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE_USE)
public #interface MyTypeAnnotation {
public String value();
}
One can then use this annotation on any type reference like e.g.:
Consumer<String> consumer = new #MyTypeAnnotation("Hello ") Consumer<String>() {
#Override
public void accept(String str) {
System.out.println(str);
}
};
Here is a complete example, that uses this annotation to print "Hello World":
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Java8Example {
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE_USE)
public #interface MyTypeAnnotation {
public String value();
}
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, new #MyTypeAnnotation("Hello ") Consumer<String>() {
#Override
public void accept(String str) {
System.out.println(str);
}
});
}
public static void testTypeAnnotation(List<String> list, Consumer<String> consumer){
MyTypeAnnotation annotation = null;
for (AnnotatedType t : consumer.getClass().getAnnotatedInterfaces()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break;
}
}
for (String str : list) {
if (annotation != null) {
System.out.print(annotation.value());
}
consumer.accept(str);
}
}
}
The output will be:
Hello World!
Hello Type Annotations!
In Java 8 one can also replace the anonymous class in this example with a lambda expression:
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, p -> System.out.println(p));
}
But since the compiler infers the Consumer type argument for the lambda expression, one is no longer able to annotate the created Consumer instance:
testTypeAnnotation(list, #MyTypeAnnotation("Hello ") (p -> System.out.println(p))); // Illegal!
One could cast the lambda expression into a Consumer and then annotate the type reference of the cast expression:
testTypeAnnotation(list,(#MyTypeAnnotation("Hello ") Consumer<String>) (p -> System.out.println(p))); // Legal!
But this will not produce the desired result, because the created Consumer class will not be annotated with the annotation of the cast expression. Output:
World!
Type Annotations!
Two questions:
Is there any way to annotate a lambda expression similar to annotating a corresponding anonymous class, so one gets the expected "Hello World" output in the example above?
In the example, where I did cast the lambda expression and annotated the casted type: Is there any way to receive this annotation instance at runtime, or is such an annotation always implicitly restricted to RetentionPolicy.SOURCE?
The examples have been tested with javac and the Eclipse compiler.
Update
I tried the suggestion from #assylias, to annotate the parameter instead, which produced an interesting result. Here is the updated test method:
public static void testTypeAnnotation(List<String> list, Consumer<String> consumer){
MyTypeAnnotation annotation = null;
for (AnnotatedType t : consumer.getClass().getAnnotatedInterfaces()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break;
}
}
if (annotation == null) {
// search for annotated parameter instead
loop: for (Method method : consumer.getClass().getMethods()) {
for (AnnotatedType t : method.getAnnotatedParameterTypes()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break loop;
}
}
}
}
for (String str : list) {
if (annotation != null) {
System.out.print(annotation.value());
}
consumer.accept(str);
}
}
Now, one can also produce the "Hello World" result, when annotating the parameter of an anonymous class:
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, new Consumer<String>() {
#Override
public void accept(#MyTypeAnnotation("Hello ") String str) {
System.out.println(str);
}
});
}
But annotating the parameter does not work for lambda expressions:
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, (#MyTypeAnnotation("Hello ") String str) -> System.out.println(str));
}
Interestingly, it is also not possible to receive the name of the parameter (when compiling with javac -parameter), when using a lambda expression. I'm not sure though, if this behavior is intended, if parameter annotations of lambdas have not yet been implemented, or if this should be considered a bug of the compiler.
After digging into the Java SE 8 Final Specification I'm able to answer my questions.
(1) In response to my first question
Is there any way to annotate a lambda expression similar to annotating
a corresponding anonymous class, so one gets the expected "Hello
World" output in the example above?
No.
When annotating the Class Instance Creation Expression (ยง15.9) of an anonymous type, then the annotation will be stored in the class file either for the extending interface or the extending class of the anonymous type.
For the following anonymous interface annotation
Consumer<String> c = new #MyTypeAnnotation("Hello ") Consumer<String>() {
#Override
public void accept(String str) {
System.out.println(str);
}
};
the type annotation can then be accessed at runtime by calling Class#getAnnotatedInterfaces():
MyTypeAnnotation a = c.getClass().getAnnotatedInterfaces()[0].getAnnotation(MyTypeAnnotation.class);
If creating an anonymous class with an empty body like this:
class MyClass implements Consumer<String>{
#Override
public void accept(String str) {
System.out.println(str);
}
}
Consumer<String> c = new #MyTypeAnnotation("Hello ") MyClass(){/*empty body!*/};
the type annotation can also be accessed at runtime by calling Class#getAnnotatedSuperclass():
MyTypeAnnotation a = c.getClass().getAnnotatedSuperclass().getAnnotation(MyTypeAnnotation.class);
This kind of type annotation is not possible for lambda expressions.
On a side note, this kind of annotation is also not possible for normal class instance creation expressions like this:
Consumer<String> c = new #MyTypeAnnotation("Hello ") MyClass();
In this case, the type annotation will be stored in the method_info structure of the method, where the expression occurred and not as an annotation of the type itself (or any of its super types).
This difference is important, because annotations stored in the method_info will not be accessible at runtime by the Java reflection API. When looking at the generated byte code with ASM, the difference looks like this:
Type Annotation on an anonymous interface instance creation:
#Java8Example$MyTypeAnnotation(value="Hello ") : CLASS_EXTENDS 0, null
// access flags 0x0
INNERCLASS Java8Example$1
Type Annotation on a normal class instance creation:
NEW Java8Example$MyClass
#Java8Example$MyTypeAnnotation(value="Hello ") : NEW, null
While in the first case, the annotation is associated with the inner class, in the second case, the annotation is associated with the instance creation expression inside the methods byte code.
(2) In response to the comment from #assylias
You can also try (#MyTypeAnnotation("Hello ") String s) ->
System.out.println(s) although I have not managed to access the
annotation value...
Yes, this is actually possible according to the Java 8 specification. But it is not currently possible to receive the type annotations of the formal parameters of lambda expressions through the Java reflection API, which is most likely related to this JDK bug: Type Annotations Cleanup. Also the Eclipse Compiler does not yet store the relevant Runtime[In]VisibleTypeAnnotations attribute in the class file - the corresponding bug is found here: Lambda parameter names and annotations don't make it to class files.
(3) In response to my second question
In the example, where I did cast the lambda expression and annotated
the casted type: Is there any way to receive this annotation instance
at runtime, or is such an annotation always implicitly restricted to
RetentionPolicy.SOURCE?
When annotating the type of a cast expression, this information also gets stored in the method_info structure of the class file. The same is true for other possible locations of type annotations inside the code of a method like e.g. if(c instanceof #MyTypeAnnotation Consumer). There is currently no public Java reflection API to access these code annotations. But since they are stored in the class file, it is at least potentially possible to access them at runtime - e.g. by reading the byte code of a class with an external library like ASM.
Actually, I managed to get my "Hello World" example working with a cast expression like
testTypeAnnotation(list,(#MyTypeAnnotation("Hello ") Consumer<String>) (p -> System.out.println(p)));
by parsing the calling methods byte code using ASM. But the code is very hacky and inefficient, and one should probably never do something like this in production code. Anyway, just for completeness, here is the complete working "Hello World" example:
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.TypeReference;
public class Java8Example {
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE_USE)
public #interface MyTypeAnnotation {
public String value();
}
public static void main(String[] args) {
List<String> list = Arrays.asList("World!", "Type Annotations!");
testTypeAnnotation(list, new #MyTypeAnnotation("Hello ") Consumer<String>() {
#Override
public void accept(String str) {
System.out.println(str);
}
});
list = Arrays.asList("Type-Cast Annotations!");
testTypeAnnotation(list,(#MyTypeAnnotation("Hello ") Consumer<String>) (p -> System.out.println(p)));
}
public static void testTypeAnnotation(List<String> list, Consumer<String> consumer){
MyTypeAnnotation annotation = null;
for (AnnotatedType t : consumer.getClass().getAnnotatedInterfaces()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break;
}
}
if (annotation == null) {
// search for annotated parameter instead
loop: for (Method method : consumer.getClass().getMethods()) {
for (AnnotatedType t : method.getAnnotatedParameterTypes()) {
annotation = t.getAnnotation(MyTypeAnnotation.class);
if (annotation != null) {
break loop;
}
}
}
}
if (annotation == null) {
annotation = findCastAnnotation();
}
for (String str : list) {
if (annotation != null) {
System.out.print(annotation.value());
}
consumer.accept(str);
}
}
private static MyTypeAnnotation findCastAnnotation() {
// foundException gets thrown, when the cast annotation is found or the search ends.
// The found annotation will then be stored at foundAnnotation[0]
final RuntimeException foundException = new RuntimeException();
MyTypeAnnotation[] foundAnnotation = new MyTypeAnnotation[1];
try {
// (1) find the calling method
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement previous = null;
for (int i = 0; i < stackTraceElements.length; i++) {
if (stackTraceElements[i].getMethodName().equals("testTypeAnnotation")) {
previous = stackTraceElements[i+1];
}
}
if (previous == null) {
// shouldn't happen
return null;
}
final String callingClassName = previous.getClassName();
final String callingMethodName = previous.getMethodName();
final int callingLineNumber = previous.getLineNumber();
// (2) read and visit the calling class
ClassReader cr = new ClassReader(callingClassName);
cr.accept(new ClassVisitor(Opcodes.ASM5) {
#Override
public MethodVisitor visitMethod(int access, String name,String desc, String signature, String[] exceptions) {
if (name.equals(callingMethodName)) {
// (3) visit the calling method
return new MethodVisitor(Opcodes.ASM5) {
int lineNumber;
String type;
public void visitLineNumber(int line, Label start) {
this.lineNumber = line;
};
public void visitTypeInsn(int opcode, String type) {
if (opcode == Opcodes.CHECKCAST) {
this.type = type;
} else{
this.type = null;
}
};
public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {
if (lineNumber == callingLineNumber) {
// (4) visit the annotation, if this is the calling line number AND the annotation is
// of type MyTypeAnnotation AND it was a cast expression to "java.util.function.Consumer"
if (desc.endsWith("Java8Example$MyTypeAnnotation;") && this.type != null && this.type.equals("java/util/function/Consumer")) {
TypeReference reference = new TypeReference(typeRef);
if (reference.getSort() == TypeReference.CAST) {
return new AnnotationVisitor(Opcodes.ASM5) {
public void visit(String name, final Object value) {
if (name.equals("value")) {
// Heureka! - we found the Cast Annotation
foundAnnotation[0] = new MyTypeAnnotation() {
#Override
public Class<? extends Annotation> annotationType() {
return MyTypeAnnotation.class;
}
#Override
public String value() {
return value.toString();
}
};
// stop search (Annotation found)
throw foundException;
}
};
};
}
}
} else if (lineNumber > callingLineNumber) {
// stop search (Annotation not found)
throw foundException;
}
return null;
};
};
}
return null;
}
}, 0);
} catch (Exception e) {
if (foundException == e) {
return foundAnnotation[0];
} else{
e.printStackTrace();
}
}
return null;
}
}
One possible work around that might be of use is to define empty interfaces that extend the interface that the lambda is going to implement and then cast to this empty interface just to use the annotation. Like so:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Consumer;
public class Main
{
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE_USE)
public #interface MyAnnotation {
public String value();
}
#MyAnnotation("Get this")
interface AnnotatedConsumer<T> extends Consumer<T>{};
public static void main( String[] args )
{
printMyAnnotationValue( (AnnotatedConsumer<?>)value->{} );
}
public static void printMyAnnotationValue( Consumer<?> consumer )
{
Class<?> clas = consumer.getClass();
MyAnnotation annotation = clas.getAnnotation( MyAnnotation.class );
for( Class<?> infClass : clas.getInterfaces() ){
annotation = infClass.getAnnotation( MyAnnotation.class );
System.out.println( "MyAnnotation value: " + annotation.value() );
}
}
}
The annotation is then available on the interfaces implemented by the class and is reusable if you want the same annotation elsewhere.

flexjson deserialize property string with dot "." inside

I'm trying to use flexjson to deserialize a string I get from a web call. The problem is that a few elements in there have a dot in the property/key for example:
[{...
"contact.name": "Erik Svensson",
"contact.mail": "erik.svensson#foo.bar",
"contact.phone": "0731123243",
...}]
Now everything else falls in place except these strings with the dots, they end up null in my target class. I'm guessing it's because it doesn't know what to map them to as I can't declare a variable in my container class that has a dot.
This is the code I'm runnign to deserialize now,
mData = new JSONDeserializer<List<Thing>>()
.use("values", Thing.class)
.deserialize(reader);
How do I modify this to catch the strings with the dot and put them in my Things class as:
String contactName;
String contactMail;
String contactPhone;
// getters&setters
Note I don't have any control over the Serialization..
OK So I've solved this but I had to abandon flexJson. Searched all over the place for a simple way but couldn't find one.
Instead I went with Jackson and this is what I ended up with:
ObjectMapper mapper = new ObjectMapper();
mThings = mapper.readValue(url, new TypeReference<List<Thing>>() {});
And in my class Thing:
#JsonProperty("contact.name")
private String contactName;
#JsonProperty("contact.mail")
private String contactMail;
#JsonProperty("contact.phone")
private String contactPhone;
// getters and setters..
If anyone knows how to do this with FlexJson feel free to post an answer, I would like to see it.
As I was curious, too, if this type of assignment can be done easily, I've played with some code, and this is what I came up with. (I'm posting it here because maybe it's helpful for somebody having some related question, or just as a point to start from.)
The PrefixedObjectFactory (see below) will cut off a fixed prefix from the JSON object's field name and use this name to find a matching bean property. The code can be easily changed to do a replacement instead (e.g. setting the first letter after a . to uppercase and remove the .)
It can be used like this:
List<Thing> l = new JSONDeserializer<List<Thing>>().use("values", new PrefixedObjectFactory(Thing.class, "contact.")).deserialize(source);
The code:
import flexjson.ObjectBinder;
import flexjson.ObjectFactory;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Type;
import java.util.Map;
public class PrefixedObjectFactory<T> implements ObjectFactory {
protected Class<T> clazz;
protected String prefix;
public PrefixedObjectFactory(Class<T> c, String prefix) {
this.clazz = c;
this.prefix = (prefix == null) ? "" : prefix;
}
#Override
public Object instantiate(ObjectBinder context, Object value, Type targetType, Class targetClass) {
try {
Class useClass = this.clazz;
T obj = (T)useClass.newInstance();
if (value instanceof Map) {
// assume that the value is provided as a map
Map m = (Map)value;
for (Object entry : m.entrySet()) {
String propName = (String)((Map.Entry)entry).getKey();
Object propValue = ((Map.Entry)entry).getValue();
propName = fixPropertyName(propName);
propValue = fixPropertyValue(propValue);
assignValueToProperty(useClass, obj, propName, propValue);
}
} else {
// TODO (left out here, to keep the code simple)
return null;
}
return obj;
} catch (Exception ex) {
return null;
}
}
protected String fixPropertyName(String propName) {
if (propName.startsWith(this.prefix)) {
propName = propName.substring(this.prefix.length());
}
return propName;
}
protected Object fixPropertyValue(Object propValue) {
return propValue;
}
protected PropertyDescriptor findPropertyDescriptor(String propName, Class clazz) {
try {
return new PropertyDescriptor(propName, clazz);
} catch (Exception ex) {
return null;
}
}
protected void assignValueToProperty(Class clazz, Object obj, String propName, Object propValue) {
try {
PropertyDescriptor propDesc = findPropertyDescriptor(propName, clazz);
if (propDesc != null) {
propDesc.getWriteMethod().invoke(obj, propValue);
}
} catch (Exception ex) {
}
}
}

Getting sub class fields using super class using reflection?

I have a class as below.
public class Emp{
private String name;
private String age;
//setters and getters
}
Have one more class below.
public class Student extends Emp{
private int marks;
//setters and getters
}
is there anyway to get the fields of a subclass using super class using java Reflection?
I need to get Student fields using Emp instance.
we can get super class fields as below:
subClass.getClass().getSuperclass().getDeclaredFields();
similarly can i get sub class fields using super class?
Is it possible?
Thanks!
I may have misunderstood your question. Do you want to do something like the following?
Emp e = new Student(...);
[do something with e]
foo = e.marks;
If yes, do it like this:
foo = ((Emp)e).marks;
However, if you want to do something like the following:
Emp e = new Emp(...);
[do something with e]
e.marks = ....
Then no, it's not possible, and I'd suspect your internal model of java's object model is either incomplete or flawed.
In theory there is a very complicated and costly way by retrieving all loaded classes and checking which of them are derived from Emp and contain the field. If the desired class wasn't loaded yet this may not help either.
Not directly, you have to write a helper method to that.
You take a class and the field name (and possibly type) as parameters, then look for that field in the given class. If you cant find it, you take the class's superclass and repeat from the beginning. You do this until you either found the field, or getSuperClass() returned null (meaning you reached the root of the inheritance tree).
This example demonstrates how to call find and call a specified method on an object. You can easily extract and adapt the logic for fields.
public static Object call(final Object instance,
final String methodName,
final Class<?>[] signature,
final Object[] args) {
try {
if (instance == null)
return null;
Class<?> instanceClass = instance.getClass();
while (instanceClass != null) {
try {
final Method method = instanceClass.getDeclaredMethod(methodName, signature);
if (!method.isAccessible())
method.setAccessible(true);
return method.invoke(instance, args);
} catch (final NoSuchMethodException e) {
// ignore
}
instanceClass = instanceClass.getSuperclass();
}
} catch (final Throwable e) {
return null;
}
return null;
}
Is it what you want? But beware of using field.setAccesible.
Parent class:
public class ParentClass {
private String parentField = "parentFieldValue";
public void printFields() throws IllegalAccessException {
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue = field.get(this);
if (fieldValue instanceof String) {
String stringValue = (String) fieldValue;
System.out.println(stringValue);
}
}
}
}
Child class:
public class ChildClass extends ParentClass {
private String childField = "childFieldValue";
}
Usage:
public class Main {
public static void main(String[] args) throws IllegalAccessException {
ParentClass pc = new ParentClass();
ChildClass cc = new ChildClass();
pc.printFields();
cc.printFields();
}
}
This is the final solution!
#NonNull
public static List<Class<?>> getSubClasses() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
String method = trace[3].getMethodName();
if (!"<init>".equals(method)) {
throw new IllegalStateException("You can only call this method from constructor!");
}
List<Class<?>> subClasses = new ArrayList<>();
for (int i = 4; i < trace.length; i++) {
method = trace[i].getMethodName();
if ("<init>".equals(method)) {
try {
subClasses.add(Class.forName(trace[i].getClassName()));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} else {
break;
}
}
return subClasses;
}
this are some examples of usage:
class a {
public a(){
print(getSubClasses());
}
}
class b extends a{
}
class c extends b{
}
And the result is
new a() -> []
new b() -> [b.class]
new c() -> [b.class, c.class]

Categories

Resources