Jaxb when use jaxb.index file - java

I was investigating object marshaling and unMarshaling using JAXB. while noticed that there is two option of getting an instance of JAXBContext.
one is based on class: JAXBContext context = JAXBContext.newInstance(ex.getClass());
Other one is based on package name: JAXBContext context = JAXBContext.newInstance(ex.getClass().getPackage().getName());
For the second way, you have to provide jaxb.index file, containing list of bean class names.
Maybe someone can explain, what is the difference between this two methods of getting JAXBContext instance? Which is better to use and when?

For the second way, you have to provide jaxb.index file, containing list of bean class names.
This is not correct. In JAXB2 this works without jaxb.index as well, the classes are "recognized" via ObjectFactory and #XmlSeeAlso.
The usual approach is to use JAXBContext context = JAXBContext.newInstance("my.package:my.another.package); as you normally want to consider all of the relevant classes and don't want to enumerate them explicitly.

Related

JAXBContext context path vs #XMLSeeAlso

I have gotten confused by these two aspects of JAXB.
As far as I understand, when passing colon separated package names as context path to newInstance method of JAXBContext, JAXB will add all the classes under these packages to its scope to be used in operations like marshall/unmarshall/validate...etc.
The #XmlSeeAlso annotation instructs the JAXBContext to bind classes. All the examples I've seen use this annotation to specify only its child classes (I couldn't tell why actually).
Below are my questions:
1- Can the #XmlSeeAlso be omitted if the classes referred by the
annotation are within the packages passed to JAXBContext newInstance
method?
2- If the above is true, then why do we need this annotation in the first
place, where we can simply pass ALL the packages containing the classes we need to the newInstance
function?
3- If the above is not true, can we only use packages containing top
level classes passed to the newInstance, and use the annotation #XmlSeeAlso to reference all
children of these classes?
1- Can the #XmlSeeAlso be omitted if the classes referred by the
annotation are within the packages passed to JAXBContext newInstance
method?
yes, #XmlSeeAlso is used to instruct JAXBContext to look for classes that is not otherwise loaded by newInstance(). To let JAXBContext to know about the subclasses used, either you can specify with #XmlSeeAlso or pass all subclasses in the newInstance method.
2- If the above is true, then why do we need this annotation in the
first place, where we can simply pass ALL the packages containing the
classes we need to the newInstance function?
#XmlSeeAlso approach is used mostly when you are creating JAXB classes on your own. When you have XSD->JAXB approach, then JAXBContext.newInstance() with all packages separated by : is preferred.

How to handle various concrete implementations of an Interface with JAX-B

I have a class that any I need to marshal to XML.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ClassToBeMarshalled {
public Interface object;
}
The Interface is implemented by a lot of concrete classes and most of them are vendor specific whose source code I don't have access to.
So my problem is:
If I try to marshal that class, JAX-B will complain that the current concrete implementation of Interface is not known in the context - In another words, the concrete class was not loaded into the context by calling JAXBContext.newInstance providing the current implementation.
The most common ways to sort out that problem are:
1) Use XMLSeeAlso - not a viable option as there are a lot of concrete classes
2) Annotate each class with #XmlRootElement - not a viable option as I don't have access to all the classes
Does anyone know a way to make JAX-B load the concrete class into its context as the need arises?
Thanks.
P.S.: I'm using JAX-B RI
You could mark your object as #XmlAnyElement(InterfaceHandler.class) where InterfaceHandler is a DomHandler capable of translating between a DOM representation and the actual implementing classes. That handler should probably store the class name when marshalling, and use that class name to create the instance when unmarshalling. It might either configure the instance manually, perhaps using some helper classes designed to work with beans, or it might use another jaxb context which includes that specifically named class and will handle that object with all its nested children.
Also have a look at the #XmlElementRef annotation. I fear that in order to make this work properly, you'd have to at least know all the implementing classes at compile time, but perhaps there is a way you can make this work for you as well, with less trouble than the generic solution outlined in the previous paragraph.

JAXB own object factory not used when unmarshalling

Image you've got auto generated beans and you want to extend them with some extra functionality. So you extend the auto generated beans and in the same file you also extend the ObjectFactory and annotate it with #XmlRegistry.
When unmarshalling some object the extended ObjectFactory ins't used and therefore the auto generated beans are created.
What step am I missing to get my custom beans created? Can I tell JAXB which Factory to use? Or does JAXB not use the factory at all?
Yes, you need to specify the ObjectFactory when configuring your Unmarshaller. I think you can use something like this to provide your own ObjectFactory implemenation:
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new MyObjectFactory());

problem subclassing ObjectFactory in jax-ws web service

In a jax-ws web service I cannot directly access the JaxbContext object. JaxbContext uses the class ObjectFactory. I tried extending it (like in Jaxb Adding Behaviors). I put generated code in ObjectFactoryBase, then added
public class ObjectFactory extends ObjectFactoryBase {//.. overriden methods}
However IllegalAnnotationsException came up when publishing to weblogic, because it cannot find a certain #XmlElementDecl present in ObjectFactoryBase. When I move the method with this #XmlElementDecl to ObjectFactory it works.
No luck with adding #XmlSeeAlso({ ObjectFactoryBase.class }) either.
Edit: I now discovered that the generated ObjectFactory is not even used by the jaxws web service. So the above error message are not so relevant any more. Any idea why it is generated but not used?
Any ideas?
JAXB ObjectFactories are strange beasts. Your question has many facets, so I'll just answer with a bullet list:
JAXB1 relied on ObjectFactory to create instances of the bound classes, but with JAXB2 everything is a POJO, and the ObjectFactory becomes mostly unnecessary. It's still generated by XJC, partly for reasons of backwards compatibility.
The annotations on an ObjectFactory are complex and non-obvious, but since it's a generated class, this usually doesn't matter, and most people don't look at it anyway.
ObjectFactory is still useful on occasion because it provides factory methods for bound classes that need a JAXBElement wrapper, and it's much easier to use the provided factory methods than to do this by hand.
The JAXWS web service may choose not to use the ObjectFactory, because it's not strictly speaking necessary. However the JAXBContext may still load and parse it, depending on how the context was initialized.
I've never tried creating an ObjectFactory myself; if the model was XJC-generated then the generated ObjectFactory is usually enough, and if you have a hand-written JAXB model, the ObjectFactory is usually completely unnecessary anyway.

Marshalling arraylist of abstract class in JAXB

I have a Java application that I want to save the data in XML instead of a database.
We decided to go with JAXB and instead of generating files based on the schema, we just added annotations to our Java files.
The issue we are running into is that we have an ArrayList of an abstract class called Node. A Node has subclasses of either Module or ScreenImage. When we marshall the arraylist, it doesn't save the type. Such as:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Module"
Does JAXB support elements that are abstract?
How do I get JAXB to save the type, so that I'm able to unmarshall it?
axtavt's suggestion is fine. I just want to add another approach. You could make use of #XmlSeeAlso, where you can declare other classes that should defined and visible to JAXBContext. You only have to make sure to declare the annotation within a class that is already visible to JAXBContext.
e.g.:
#XmlRootElement
#XmlSeeAlso({Node.class, Module.class, ScreenImage.class})
class SomeContent {
private List<Node> nodes;
///... accessors
}
It should work fine.
Note that if your Module and ScreenImage are not statically accessible from the bound classes (i.e. the classes specified in JAXBContext.newInstance(...)), they should be bound explicitly (add them to the JAXBContext.newInstance(...) parameters).

Categories