How to get elements from autogenerated #XmlSeeAlso? - java

I'm generating java classes from wsdl using cxf / jaxb and the configuration <jaxb:globalBindings generateElementProperty="false">, which gives me the advantage to directly get the elements by getters and setters from the xml, not having to instantiate a JAXBElement explicit every time.
One element I want to access is autogenrated as #XmlSeeAlso.
Problem: the autogenerated class does not contain any getter for this class! How can I at all receive the properties inside there if there's no getter created??
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Fare", propOrder = {
"some",
"props"
})
#XmlSeeAlso({
AnySubType.class
})
#Generated
public class AutoGeneratedClass {
//there is no getAnySubType() method!
//only getters for some, props etc.
}
Where as:
#Generated
public class AnySubType
extends AutoGeneratedClass
{

I will attempt to address the points you raised:
//there is no getAnySubType() method!
There wouldn't be. It isn't common object-oriented practice to have a method on a super class that returns an instance of a specific subclass.
well somehow a few values that I need to get from the xml are inside
hte AnySubType, but a getter for that type is missing so I cannot
access them...
You would need to cast the instance of AutoGeneratedClass to AnySubType to access the subclass methods.

Related

Get outer class by member annotation

I have a class with custom annotation for one of class field:
public class Test {
#CustomAnnotation
private String name;
...
}
I just want to know if it possible to get Class<Test> by this annotation? Can't find any suitable api..
public Class<?> getOuterClass(CustomAnnotation annotation) {
...
}
#CustomAnnotation is declared as #Retention(RetentionPolicy.RUNTIME)
No, annotation does not store any data about where it was declared.
Also annotation can work just like any normal interface, so someone can implement annotation in class an make instances of it that were never used as annotations.
You need either include that information yourself, by adding parameter to annotation and then using it #CustomAnn(Test.class) or when reading annotations just remember and include that information yourself in some other object.

IReport and getter between classes

I'm using IReport (JasperStudio plugin for Eclipse) and I'm trying to create a report with a JavaBean as source.
Suppose I have these two classes:
public class MyClass {
private String myClassAttribute;
// getter and setter for myClassAttribute
}
public class AnotherMyClass {
private String anotherMyClassAttribute;
private MyClass myClass;
// getter and setter for anotherMyClassAttribute
// getter and setter for myClass
}
If I choose AnotherMyClass as JavaBeanSource I can set only fields from that class (anotherMyClassAttribute), I didn't find a way to set a text to getMyClass().getmyClassAttribute().
Do JavaBeans stop at level one or is there a way to use attribute from other classes between references?
Thanks.
In report define field $F{myClass} with type MyClass
In text field use expression $F{myClass}.getMyClassAttribute()
No, it doesn't stop at level one, you may go as deep as you want. You may use the attribute like myClass.myClassAttribute. And for setting a value to it, myClass.myClassAttribute = "some value"

Creating custom annotations

How does annotation work with Java? And how can I create custom annotations like this:
#Entity(keyspace=':')
class Student
{
#Id
#Attribute(value="uid")
Long Id;
#Attribute(value="fname")
String firstname;
#Attribute(value="sname")
String surname;
// Getters and setters
}
Basically, what I need to have is this POJO be serialized like this when persisted:
dao.persist(new Student(0, "john", "smith"));
dao.persist(new Student(1, "katy", "perry"));
Such that, the actual generated/persisted object is a Map<String,String> like this:
uid:0:fname -> john
uid:0:sname -> smith
uid:1:fname -> katy
uid:1:sname -> perry
Any ideas how to implement this?
If you create custom annotations you will have to use Reflection API Example Here to process them.
You can refere How to declare annotation.
Here is how example annotation declaration in java looks like.
import java.lang.annotation.*;
/**
* Indicates that the annotated method is a test method.
* This annotation should be used only on parameterless static methods.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Test { }
Retention and Target are known as meta-annotations.
RetentionPolicy.RUNTIME indicates that you want to retain the annotation at runtime and you can access it at runtime.
ElementType.METHOD indicates that you can declare annotation only on methods similarly you can configure your annotation for class level, member variable level etc.
Each Reflection class has methods to get annotations which are declared.
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
Returns this element's annotation for the specified type if such an annotation is present, else null.
public Annotation[] getDeclaredAnnotations()
Returns all annotations that are directly present on this element. Unlike the other methods in this interface, this method ignores inherited annotations. (Returns an array of length zero if no annotations are directly present on this element.) The caller of this method is free to modify the returned array; it will have no effect on the arrays returned to other callers.
You will find these methods present for Field, Method,Class classes.
e.g.To retrieve annotations present on specified class at run time
Annotation[] annos = ob.getClass().getAnnotations();

Why does JAXB need a no arg constructor for marshalling?

If you try to marshal a class which references a complex type that does not have a no-arg constructor, such as:
import java.sql.Date;
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
Date d; //java.sql.Date does not have a no-arg constructor
}
with the JAXB implementation that is part of Java, as follows:
Foo foo = new Foo();
JAXBContext jc = JAXBContext.newInstance(Foo.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, baos);
JAXB will throw a
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions java.sql.Date does not have a no-arg default constructor
Now, I understand why JAXB needs a no-arg constructor on unmarshalling - because it needs to instantiate the object. But why does JAXB need a no-arg constructor while marshalling?
Also, another nit, why does Java's JAXB implementation throw an exception if the field is null, and isn't going to be marshalled anyway?
Am I missing something or are these just bad implementation choices in Java's JAXB implementation?
When a JAXB (JSR-222) implementation initializes its metadata it ensures that it can support both marshalling and unmarshalling.
For POJO classes that do not have a no-arg constructor you can use a type level XmlAdapter to handle it:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
java.sql.Date is not supported by default (although in EclipseLink JAXB (MOXy) it is). This can also be handled using an XmlAdapter specified via #XmlJavaTypeAdapter at field, property, or package level:
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html
http://blog.bdoughan.com/2011/01/jaxb-and-datetime-properties.html
Also, another nit, why does Java's JAXB implementation throw an
exception if the field is null, and isn't going to be marshalled
anyway?
What exception are you seeing? Normally when a field is null it is not included in the XML result, unless it is annotated with #XmlElement(nillable=true) in which case the element will include xsi:nil="true".
UPDATE
You could do the following:
SqlDateAdapter
Below is an XmlAdapter that will convert from the java.sql.Date that your JAXB implementation doesn't know how to handle to a java.util.Date which it does:
package forum9268074;
import javax.xml.bind.annotation.adapters.*;
public class SqlDateAdapter extends XmlAdapter<java.util.Date, java.sql.Date> {
#Override
public java.util.Date marshal(java.sql.Date sqlDate) throws Exception {
if(null == sqlDate) {
return null;
}
return new java.util.Date(sqlDate.getTime());
}
#Override
public java.sql.Date unmarshal(java.util.Date utilDate) throws Exception {
if(null == utilDate) {
return null;
}
return new java.sql.Date(utilDate.getTime());
}
}
Foo
The XmlAdapter is registered via the #XmlJavaTypeAdapter annotation:
package forum9268074;
import java.sql.Date;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
#XmlJavaTypeAdapter(SqlDateAdapter.class)
Date d; //java.sql.Date does not have a no-arg constructor
}
To answer your question: I think this is just poor design in JAXB (or perhaps in JAXB implementations). The existence of a no-arg constructor gets validated during creation of the JAXBContext and therefore applies regardless if you want to use JAXB for marshalling or unmarshalling. It would have been great if JAXB would defer this type of check to JAXBContext.createUnmarshaller(). I think it would be interesting to dig into if this design is actually mandated by the spec or if it is an implementation design in JAXB-RI.
But there's indeed a workaround.
JAXB doesn't actually need a no-arg constructor for marshalling. In the following I'll assume you are using JAXB solely for marshalling, not unmarshalling. I also assume that you have control over the immutable object which you want to marshall so that you can change it. If this is not the case then the only way forward is XmlAdapter as described in other answers.
Suppose you have a class, Customer, which is an immutable object. Instantiation is via Builder Pattern or static methods.
public class Customer {
private final String firstName;
private final String lastName;
private Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// Object created via builder pattern
public static CustomerBuilder createBuilder() {
...
}
// getters here ...
}
True, that by default you cannot get JAXB to unmarshall such an object. You'll get error "....Customer does not have a no-arg default constructor".
There are at least two ways of solving this. They both rely on putting in a method or constructor solely to make JAXB's introspection happy.
Solution 1
In this method we tell JAXB that there's a static factory method it can use to instantiate an instance of the class. We know, but JAXB doesn't, that indeed this will never be used. The trick is the #XmlType annotation with factoryMethod parameter. Here's how:
#XmlType(factoryMethod="createInstanceJAXB")
public class Customer {
...
private static Customer createInstanceJAXB() { // makes JAXB happy, will never be invoked
return null; // ...therefore it doesn't matter what it returns
}
...
}
It doesn't matter if the method is private as in the example. JAXB will still accept it. Your IDE will flag the method as unused if you make it private, but I still prefer private.
Solution 2
In this solution we add a private no-arg constructor which just passes null into the real constructor.
public class Customer {
...
private Customer() { // makes JAXB happy, will never be invoked
this(null, null); // ...therefore it doesn't matter what it creates
}
...
}
It doesn't matter if the constructor is private as in the example. JAXB will still accept it.
Summary
Both solutions satisfies JAXB's desire for no-arg instantiation. It is a shame that we need to do this, when we know by ourselves that we only need to marshal, not unmarshal.
I have to admit that I do not know to what extent this is a hack that will only work with JAXB-RI and not for example with EclipseLink MOXy. It definitely works with JAXB-RI.
You seem to be under the impression that the JAXB introspection code will have action specific paths for initialization. if so, that would result in a lot of duplicate code and would be a poor implementation. i would imagine that the JAXB code has a common routine which examines a model class the first time it is needed and validates that it follows all the necessary conventions. in this situation, it is failing because one of the members does not have the required no-arg constructor. the initialization logic is most likely not marshall/unmarshall specific and also highly unlikely to take the current object instance into account.
Some enterprise and Dependency Injection frameworks use reflection Class.newInstance() to create a new instance of your classes. This method requires a public no-arg constructor to be able to instantiate the object.

Java reflection (and annotations): bean.class is empty

I jave a simple Java bean with 4 attributes, getter/setter, and some overided methods like toString, equals and hashCode.
Above every Attribute is a custom Annotation:
import java.lang.annotation.*;
import java.lang.annotation.RetentionPolicy;
#Target(ElementType.FIELD)
#Retention( RetentionPolicy.RUNTIME )
public #interface DAOProperty {
String name();
String type();
boolean identifier() default false;
}
/** The id. */
#DAOProperty(name = "id", type = "long", identifier = true)
private long id;
If I pass the bean.class to another method
generateEntity(User.class);
...
private static MEntity generateEntity(Class<?> bean) {...}
and debug it, it seems to bee empty, except for the class name. All arrays like methods, annotations and fields are of zero size.
Where did I go wrong?
Use beanClass.getDeclaredFields() instead of getFields(). Then iterate the array and for each Field call getAnnotations()
getFields() (and the similar methods) return only the public members.
Anyway, why don't you use JPA, instead of creating your own annotations and annotation processors?
Don't look at the internal fields of the class. They aren't relevant for you. The only thing that should interest you are the return values of the methods. It's very likely that java.lang.Class uses those fields to store information that is created on-demand.
In that case looking at the fields of the Class object at runtime won't tell you the right values (at least not always). Inspecting the return values of the desired methods, however should give the right values.

Categories

Resources