What is the ObjectFactory role during JAXB-Unmarshalling? - java

I'm using JAXB 2.2.2 to parse a simple XML-REST stream. This is the piece of code:
JAXBContext jc = JAXBContext.newInstance( "com.example.entities" );
Unmarshaller u = jc.createUnmarshaller();
r = (Response )u.unmarshal( inputStream );
ObjectFactory class:
#XmlRegistry
public class ObjectFactory {
public Response createRsp() {
return new Response();
}
}
Response class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="rsp")
#XmlType
public class Response { ... }
The "com.example.entities" must contain the ObjectFactory class or jaxb.index. I would like to use the ObjectFactory class in order to decide some pojo initialization, but these class is never used: the Response class is always instantiated by class.newInstance() directly.
Is there something wrong in this?

You can leverage the #XmlType annotation to control how the objects are created:
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createRsp")
public class Response {
}
For More Information
http://bdoughan.blogspot.com/2011/06/jaxb-and-factory-methods.html

The ObjectFactory class generated by the XJC compiler is not useful to the factoryClass and factoryMethod #XmlType annotation because the factoryMethod must be a static no-arg method and the XJC generate instance methods.

Related

JAXB adding annotated classes with new JAR

Have following situation:
A class with a List field like:
#XMLType
#XMLAccessType(XMLAccessorType.FIELD)
#XMLRootElement(name = "container")
public class ListContainer {
#XMLElementWrapper(name="elements")
private List<Element> elements = new ArrayList<>();
....
}
The Element class is an abstract JAXB annotated class with #XMLRootElement annotation like:
#XMLType
#XMLRootElemenent
public abstract class Element {
....
}
These classes define some kind of Framework and users shall be able to add own implementations of Element class within own JAR packages. What I would like to achieve, is that after unmarshalling I would have in elements field of the instance of class ListContainer class instances which were introduced as extensions to the framework. For instance let say there is a class DummyElement in some other ext1.jar, which is in the class path, and it looks like following:
#XMLType
#XMLRootElement(name = "dummy")
public class DummyElement extends Element {
....
}
in ext2.jar I will have EasyElement like:
#XMLType
#XMLRootElement(name = "easy")
public class EasyElement extends Element {
...
}
in xml there on the place I would have something like:
<container>
<elements>
<dummy>....</dummy>
<easy>...</easy>
<easy>....</easy>
<dummy>...</dummy>
</elements>
</container>
expected result should be, that unmarshalled instance of ListContainer class will have 2 DummyElement instances and 2 EasyElement instances in elements fields.
So far if I leave ListContainer class annotated like this - I will have nothing in list. If I annotated with #XMLAnyElement(lax=true) then I will have ElementNSImpl instances.
Thank you for ideas in advance.
Update:
The solution is in control over creation of JAXBContext. I created service interface, which delivers me a list of classes required for context like:
public interface XMLContextProvider {
Set<Class> getJAXBContextClasses();
}
Then I created in framework a class which implements this interface and lists all classes I require from side of the framework. The same was done for extensions. The classes registered in META-INF/services - see ServiceLoader. Then I created a utility class, which utilises the ServiceLoader to find all providers and creates the JAXBContext with the list of all classes gathered from all providers. With this context it is possible to Marshal and Unmarshal. Additionally as I utilise JAX-RS I have created a Resolver for JAXBContext:
#Provider
public class XMLContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext ctx;
public XMLContextResolver() {
ctx = <here goes call to utility class>
}
#Override
public JAXBContext getContext(Class<?> type) {
if (this.classes.contains(type)) {
return this.ctx;
}
return null;
}
}

How to marshal xml to a jaxb wrapper class?

<myresponse>
<field1></field1>
<field2></field2>
//...
</myresponse>
#XmlRootElement(name = "myresponse")
#XmlAccessorType(XmlAccessType.FIELD)
public class MyResponse {
//fields
}
The following would work fine:
JAXBContext jaxbContext = JAXBContext.newInstance(MyResponse.class);
Problem: I'd like to create a wrapper class around that response, and marshall it into this new class. But how, if the XML structure remains the same?
public class CustomResponse {
#XmlElement(name = "myresponse")
private MyResponse myresponse;
//some more DTO fields or bean logic
}
JAXBContext.newInstance(CustomResponse.class);
How can I tell jaxb to actually map anything from the xml response into the #XmlElement(name = "myresponse") object, if possible?
Sidenote: I have no control of the xml received.

Extending the ObjectFactory in EclipseLink MOXy

I have a JAXB model generated from MOXy's version of XJC. The xjc:superclass tag was used in the binding's file so all the objects extend a common class.
package my.package
//Base.java
#XmlTransient
public class Base {
//...
}
//MyTag.java (generated from XJC)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {/*...*/})
#XmlRootElement(name = "myTag")
public class MyTag extends Base {
//...
}
The model nor the schema can be changed, but I can change the Base class.
I need to extend MyTag, as well as some other classes in the model, so I can customize its method behaviors from the Base class. So I extended MyTag and the other domain classes that needed custom behavior, as well as the ObjectFactory. These classes exist in a separate Java package.
package my.extended.package
//MyTagExtended.java
public class MyTagExtended extends MyTag {
//...
}
//CustomObjectFactory.java
public class CustomObjectFactory extends ObjectFactory {
//...
#Override
public MyTagExtended createMyTag() {
return new MyTagExtended();
}
//...
}
Application code:
package application
//Application.java
System.setProperty("org.eclipse.persistence.moxy.annotation.xml-value-extension", "true");
JAXBContext jc = (JAXBContext) JAXBContext.newInstance(XPSObjectFactory.class);
JAXBUnmarshaller u = jc.createUnmarshaller();
return u.unmarshal(xmlFile);
The problem I'm having is MOXy seems to randomly decide whether to call the CustomObjectFactory's or ObjectFactory's methods.
In the CustomObjectFactory class, if I have only one overridden method, that method is always called. However, when I put in more, MOXy seems to randomly decide whether or not to call ObjectFactory's method's or CustomObjectFactory's.
When I use the same sort of setup with the Oracle's JAXB implementation that's supplied with the JDK, it works fine. The CustomObjectFactory's methods are always called.
Is there a configuration that needs to be set? How can I configure MOXy to always use my CustomObjectFactory's methods?
EDIT:
To clarify, here's how I made the same scenario work with Oracle's JAXB implementation:
JAXBContext jc = JAXBContext.newInstance("my.package");
Unmarshaller u = jc.createUnmarshaller();
u.setProperty("com.sun.xml.internal.bind.ObjectFactory", new CustomObjectFactory());
I tried to set the ObjectFactory property on the MOXy unmarshaller, but it threw an exception.
I found a workaround for MOXy. I found some information on XmlClassExtractor, which can be used to specify which class to instantiate. Since I can't modify the domain objects, an XML file was used instead.
I kept the extended classes the same, but modified Application.java and wrote a binding.xml file and a ClassExtractor:
package application
//Application.java
System.setProperty("org.eclipse.persistence.moxy.annotation.xml-value-extension", "true");
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "classExtractor.xml");
JAXBContext jc = (JAXBContext) JAXBContextFactory.createContext(new Class[]{CustomObjectFactory.class}, properties);
JAXBUnmarshaller u = jc.createUnmarshaller();
return u.unmarshal(xmlFile);
classExtractor.xml:
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="my.package"
version="2.3">
<java-types>
<java-type name="MyTag">
<xml-class-extractor class="my.extended.package.MyTagClassExtractor"/>
</java-type>
</java-types>
</xml-bindings>
Class extractor:
package my.extended.package
//MyTagExtractor.java
public class MyTagExtractor extends ClassExtractor {
#Override
public Class<? extends Base> extractClassFromRow(Record databaseRow, Session session) {
return MyTagExtended.class;
}
}
This seems to work, but it's quirky and prone to programmer errors since multiple source files have to be edited to get the desired functionality. Oracle's ObjectFactory property on the unmarshaller was very easy and streamlined. Does anyone have a better answer?

Is it possible to programmatically configure JAXB?

Say I have two JavaBeans Person and Address.
If I create a list of Person objects, I'd like to marshal to something like this:
<persons>
<person>...</person>
</persons>
It's possible to use the technique described here:
Using JAXB to unmarshal/marshal a List<String>
By annotating JaxbList with #XmlRootElement(name = "persons") and #XmlElement(name = "person"), then it's possible to marshal to the XML above.
But, it'd be nice to be able to reuse the same JaxbList<T> class to also marshal a list of Address objects. And in reality, I will have many other types of beans. I can go with something like:
<list>
<item xsi:type="person" xmlns:xsi="http://www.w2.org/2001/XmlSchema-instance"></item>
</list>
But, ideally, it'd be nice to have it replace "list" with the plural version of class name and "item" with the class name.
So, is it possible to programmatically configure the JaxbContext or something during runtime and essentially set the value of the name inside #XmlRootElement and #XmlElement?
Or any other way to get this working without having to write a separate implementation of JaxbList for every bean type? Maybe XmlJavaTypeAdapter can achieve this sort of thing?
Update
#Blaise Doughan's solution accepted below works great. For my use case, I needed to go straight from Java object to XML, here's what worked (note this is not my full implementation, it's sort of just pseudo code for demonstration):
//JAXBContext is thread safe and so create it in constructor or
//setter or wherever:
...
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
...
public String marshal(List<T> things, Class clazz) {
//configure JAXB and marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//Create wrapper based on generic list of objects
Wrapper<T> wrapper = new Wrapper<T>(things);
JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);
StringWriter result = new StringWriter();
//marshal!
m.marshal(wrapperJAXBElement, result);
return result.toString();
}
You could create a generic Wrapper object like the following:
Wrapper
You could create a generic wrapper class with a List property annotated with #XmlAnyElement(lax=true). The type of the object used to populate this list will be based on its root element (see: http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).
package forum13272288;
import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
public class Wrapper<T> {
private List<T> items = new ArrayList<T>();
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Address
You will need to annotate the possible contents of the list with #XmlRootElement.
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Address {
}
Person
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Person {
}
Demo
The demo code below demonstrates how to use the Wrapper class. Since the root element can be different you will need to specify that you want to unmarshal to the wrapper class. Alternatively you could leverage the #XmlElementDecl annotation to associate multiple root elements with the wrapper class (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).
package forum13272288;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, Person.class, Address.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StreamSource personsXML = new StreamSource("src/forum13272288/persons.xml");
JAXBElement<Wrapper> wrapper1 = unmarshaller.unmarshal(personsXML, Wrapper.class);
marshaller.marshal(wrapper1, System.out);
StreamSource addressesXML = new StreamSource("src/forum13272288/addresses.xml");
JAXBElement<Wrapper> wrapper2 = unmarshaller.unmarshal(addressesXML, Wrapper.class);
marshaller.marshal(wrapper2, System.out);
}
}
Output
Below is the output from running the demo code. The files persons.xml and addresses.xml look just like there corresponding output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persons>
<person/>
<person/>
</persons>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addresses>
<address/>
<address/>
</addresses>
For More Information
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html

Can a JAXB-annotated class known the JAXBContext or JAXBIntrospector instance?

Can a JAXB-annotated class known the JAXBContext or JAXBIntrospector instance which is used to marshal/unmarshal the class?
Something like:
#XmlType
class Hello {
#XmlAttribute
private String name = "";
// called by JAXB
public Hello(){
JAXBIntrospector i = [some-magic-utility-class].getCurrentIntrospector();
}
}
More specific: I need the class instance to know (when being marshalled/unmarshalled) which other classes are known to the current JAXBContext.

Categories