Adding behaviour to JAXB generated class - java

I have to add little bussiness logic in my jaxb generated classes. For example, I have following XMLs:
<vehicle>
<car id="20" make="ABC"/>
</vehicle>
<vehicle>
<motorcycle id="05" make="XYZ"/>
<vehicle>
<vehicle>
<truck id="34" make="UVW"/>
</vehicle>
And I generate XSD for these.
Now what I have to achieve is during unmarshalling of any XML of these type (that is whenever setters of car, motorcycle or truck is envoked, it should also set the vehicle type which I don't want to add as an attribute in the XML).
Or after unmarshalling (any way by which I can know the QName of sub element).
I have tried How can I extend Java code generated by JAXP-cxf or Hibernate tools?, but the overriden setters were never called.

JAXB has a "post construct" facility (see javadoc). Just add something like this to your annotated class:
void afterUnmarshal(Unmarshaller, Object parent) {
vehicle.setType(..); // your logic here
}

You can create a JAXB extension. But that sounds like an overhead to me - you could simple invoke an initializer whenever you unmarshal a JAXB object. Something like:
public class Initializer {
public static void initialize(Vehicle vehicle) {
vehicle.setType(..); // your logic here
}
}
And call Initializer.initialize(unmarshalledObject)

Related

Design pattern to use to populate multiple class fields

I have a use case where I have to parse an XML document and fill details in a corresponding fields of an internal model POJO Details.
Let's say, the XML look like following -
<?xml version="1.0" encoding="UTF-8"?>
...
<TagA>
<Child1>
...
</Child1>
<Child2>
...
</Child2>
</TagA>
...
<TagB>
<Child1>
...
</Child1>
<Child2>
...
</Child2>
</TagB>
....
And the internal model POJO looks like following -
public class Details {
...
Set<TagAInfo> tagAInformation;
Set<TagBInfo> tagBInformation;
...
}
The XML has multiple fields like TagAs, TagBs etc.
Current implementation: So currently there is a mapper/parser class(let's say Mapper.java) that calls multiple methods like mapTagAInfo(details, xmlRootElement), mapTagBInfo(details, xmlRootElement) etc. on details(i.e. instance of Details.java) something like following -
public class Mapper {
....
public Details mapInfo(XmlElement xmlRootElement) {
Details details = new Details();
mapTagAInfo(details, xmlRootElement)
mapTagBInfo(details, xmlRootElement)
mapTagCInfo(details, xmlRootElement)
....
return details;
}
private void mapTagAInfo(details, xmlRootElement) {
stp1: Extract <TagA> tag element info using a utility which reads the xml document
stp2: use the stp1 info and convert to internal model POJO TagAInfo and
add to details (details.addTagAInfo(tagAInfo))
}
Question: The current implementation makes the code look very ugly(as in a single class Mapper.java multiple things are happening), so wanted to know if I can use some design pattern to improve the code ? If so, please suggest what design pattern to use and an example explaining the design pattern usage would help a lot.
Update: The project uses Dom4j to read XML. Also, the question was more around handling the responsibilities of the mapper class. As one can see, it has lot of functions like mapTagAInfo, mapTagBInfo etc. Wanted to know if I should delegate the responsibilities to different classes - eg. TagAInfoEnricher, TagBInfoEnricher etc. If yes, then how to do it, what design pattern to use etc. ?
Try Unmarshalling with JAXB.
Examples:
https://howtodoinjava.com/jaxb/jaxb-unmarshaller-example/
https://howtodoinjava.com/jaxb/read-xml-to-java-object/
I usually like using the builder pattern, Lombok #Builder annotation, to create my objects.
#Builder
#Getter
class Details implements Serializable {
Set<TagAInfo> tagAInformation;
Set<TagBInfo> tagBInformation;
public static void mapTagAInfo(details, xmlRootElement) {
...
}
}
The other design pattern I like to use is creating Static method inside the class to construct my objects especially DTOs
Don't know if this helps

accept argument with object which has a specific annotation

PROBLEM
If I generate a jaxb object from an XML root element named 'Message' it produces the following class:
#XmlRootElement(name = "Message")
public class Message{
...
}
I have a method with the following definition:
public void doSomething(Object rootElement) {
...
}
This method is part of a framework used by 30+ developers. The method only accepts a class with the annotation #XmlRootElement. Developers sometimes miss this crucial javadoc and pass a non #XmlRootElement object to the method giving errors during runtime. As the generated class has no superclass, no interfaces, etc I have to accept an Object as argument.
QUESTION
Is it not possible to enforce this check compile time ?
ALREADY TRIED
I've looked into (via jaxb binding file) letting generated classes implement an interface and then accept this interface as argument, however it cannot (in an automated manner) be applied to only the root element class.
We have several hundred different XML Schema files from which we generated in an automated manner.

Using JAXB with JgraphX?

I have some entities mapped with JAXB annotations to turn them into xml, but within those entities mxCell there is an object, how can I map this object without adding annotations in the code library JgraphX​​?
There is my Objeto class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlSeeAlso({mxCell.class})
public abstract class ObjetoImpl implements Serializable, Objeto {
#XmlAttribute
protected String nome;
#XmlAnyElement
protected mxCell cell;
#Override
public String toString() {
return this.nome;
}
}
It's give me the following exception:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
com.mxgraph.model.mxICell is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at com.mxgraph.model.mxICell
at public com.mxgraph.model.mxICell com.mxgraph.model.mxCell.getParent()
at com.mxgraph.model.mxCell
at #javax.xml.bind.annotation.XmlSeeAlso(value=[class com.mxgraph.model.mxCell])
at ardis.model.conceitual.atributo.Atributo
at protected java.util.List ardis.model.conceitual.ObjetoWithAttributeImpl.attributes
at ardis.model.conceitual.ObjetoWithAttributeImpl
at ardis.model.conceitual.entidade.Entidade
at #javax.xml.bind.annotation.XmlSeeAlso(value=[class ardis.model.conceitual.entidade.Entidade])
at ardis.model.conceitual.ModeloConceitual
That exception occurs when the implementation of the interface is not correctly mapped with Jaxb, but i don't want enter into the jgraphx library and modify it
There are a couple of ways that you can handle this use case:
Option #1 - Specify the Impl Class with #XmlElement
For fields/properties that are of an interface type can use the #XmlElement annotation to specify the concrete implementation.
#XmlElemen(type=mxCellImpl.class)
protected mxCell cell;
For More Information
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
Option #2 - Use an XmlAdapter
An XmlAdapter allows you to convert an unmappable object to a mappable one during the marshalling/unmarshalling process. You could use this to convert JgraphX into your own domain object.
For More Information
http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html
I could not convert the objects of the program who had a mxCell inside using JAXB, so the solution for me was use the JgraphX "getXml" to convert the graph elements and the values of each cell. After that I get the value of the cells and use at my code.
The code to pass the graph to xml:
mxCodec codec = new mxCodec();
String xml = mxXmlUtils.getXml(codec.encode(graph.getModel()));

Using a JAXB XmlAdapter to adapt an entire list without a wrapper element

I'm trying to use an XmlAdapter to unmarshal XML into a custom collection type (that intentionally does not implement the Collection interface due to a conflicting restriction imposed by another use of the class, which unfortunately fails if the class contains any collections). The difficulty is that the XML nodes that will be placed into the collection are not wrapped in a wrapper element.
Here's what my XML essentially looks like:
<xml>
<foo></foo>
<bar>1</bar>
<bar>2</bar>
</xml>
I can make JAXB unmarshall this to the following POJO:
class Naive {
Foo foo;
List<Bar> bar;
}
However, what I want to do is the following:
class Bars {
// There will be a fixed number of these, known in advance
Bar bar1;
Bar bar2;
}
class BarsAdapter extends XmlAdapter<ArrayList<Bar>, Bars> {
...
}
class Custom {
Foo foo;
#XmlJavaTypeAdapter(BarsAdapter.class) // won't work
Bars bar;
}
As you can see, I wrote an XmlAdapter that wants to adapt the entire list, not the individual elements, but it never gets invoked for unmarshalling. (It does get invoked for marshalling.)
If my XML contained a wrapper around the <bar> elements, then I know how to solve this:
<xml>
<foo></foo>
<wrapper>
<bar>1</bar>
<bar>2</bar>
</wrapper>
</xml>
because then I could annotate the bars field with #XmlElement(id="wrapper") and the adapter would get correctly called. However, the XML schema comes from an RFC and is entirely unchangeable, so I am stuck as to how to proceed. Any ideas appreciated!

#XmlJavaTypeAdapter and wrappers

I don't want use XmlJavaTypeAdapter annotations with XmlAdapter's class in my code directly.
So, I wrote some wrapper:
class BinderWrapper<MODEL, BEAN> extends XmlAdapter<BEAN, MODEL>{
private final Binder<MODEL, BEAN> target;
private BinderWrapper(Binder<MODEL, BEAN> target){
this.target = target;
}
static <MODEL, BEAN> BinderWrapper<MODEL, BEAN> createInstance(Binder<MODEL, BEAN> binder){
return new BinderWrapper<MODEL, BEAN>(binder);
}
#Override
public MODEL unmarshal(BEAN v) throws Exception {
return target.unBean(v);
}
#Override
public BEAN marshal(MODEL v) throws Exception {
return target.toBean(v);
}
}
that's wrappes my binders like XmlAdapter s. All my binders will implements Binder interface
public interface Binder<MODEL, BEAN> {
MODEL unBean(BEAN bean);
BEAN toBean(MODEL model);
}
But there is a problem. #XmlJavaTypeAdapter require XmlAdapter class without any wrapper. How I can solve this problem? - use other annotation / write some config / write some magic annotation /..
Thanks.
upd
I have model classes that aren't JavaBeans. So I want do some two step mapping : in beans and than into xml. I want do first step with annotations too. Probably I well need this beans not only for JAXB. The real question is : can I do first step with some non JAXB annotations?
I have model classes that aren't
JavaBeans. So I want do some two step
mapping : in beans and than into xml.
I want do first step with annotations
too. Probably I well need this beans
not only for JAXB. The real question
is : can I do first step with some non
JAXB annotations?
The XmlAdapter provides the two step mapping you are looking for. If you look at the example linked below Map is the object that is not a Java Bean. What the XmlAdapter does is convert it to a Java Bean that can be mapped.
You may find it easier to use the #XmlJavaTypeAdapter annotation at the type level rather than the property level. When used at the type level you are saying everyone that references that class should use the adapter instead of per property. See my post on JAXB and Immutable Objects for a type level example.
For more information see:
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html

Categories