Problems with JAXB marshalling lists of non-annotated objects - java

I've got a bit of an issue with unmarshalling a list of closed objects using JAXB (closed in the sense that I cannot add JAXB annotation) . Basically, my XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<document>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<!-- SNIP! -->
</rdf:RDF>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<!-- SNIP! -->
</rdf:RDF>
</document>
And my Document class is:
#XmlRootElement(name = "document", namespace = Namespace.DEFAULT_NAMESPACE)
#XmlAccessorType(XmlAccessType.FIELD)
public class Document {
#XmlJavaTypeAdapter(ModelAdapter.class)
#XmlElement(name = "RDF", namespace = Namespace.RDF_NAMESPACE)
private List<Model> models;
....
Where Model is a class from a framework that I cannot add JAXB annotations to, hence the adaptor.
The implementation of ModelAdapter is as follows:
public class ModelUnmarshalAdapter extends ModelAdapter<Object, Model> {
#Override
public Model unmarshal(final Object v) throws Exception {
// Turn incoming Node into a Model object
Model model = convert(v);
return model;
}
....
}
When I unmarshal the XML I'm finding ModelUnmarshalAdapter.unmarshal() is being called twice as expected (due to the 2 RDF elements in the XML), but the Document instance models property is always null. It's like it doesn't instantiate the necessary list instance.
Any ideas would be greatly appreceiated.
Thanks
Nick

After much trial and error it turns out the solution was to subclass the closed object concrete class (in my case ModelCom, which implements the Model interface) and add the #XmlJavaTypeAdapter to that
#XmlJavaTypeAdapter(ModelAdapter.class)
public class MyModel extends ModelCom {
public Model(Graph base) {
super(base);
}
public Model(Graph base, Personality<RDFNode> personality) {
super(base, personality);
}
}
The Document class is now simply
public class Document {
#XmlElement(name = "RDF", namespace = Namespace.RDF_NAMESPACE)
private List<MyModel> models;

Related

How to write multiple XML files for child beans in Spring Batch ItemWriter?

I have a batch job that retrieves records from a database, processes them, and passes the result into the writer. The bean passed to the writer has four fields that need to be written to separate xml files. One of the fields is a bean representing the original record, and the other three fields are collections of child elements associated with the record.
I initially tried to use jackson to parse the beans and generate the files, but I found that approach had trouble when applied to the batch model.
Next, I've shifted to using StaxEventItemWriters for each child field, which individually seem perfectly adequate, but I'm having trouble implementing a writer that can handle all the various sub-types. I've looked into the CompositeItemWriter and ClassifierCompositeItemWriter, but they seem more suited to having multiple writers for the same type of bean, whereas I need multiple writers appropriate for differing types. Any advice would be greatly appreciated!
Domain example:
public class MyBean {
private RecordBean recordBean;
private List<ChildTypeA> aBeans;
private List<ChildTypeB> bBeans;
private List<ChildTypeC> cBeans;
}
#XmlRootElement(name = "RECORD")
public class RecordBean extends MyAbstractBean {
#XmlElement(name = "ID")
private String recordId;
#XmlElementWrapper(name = "A_CHILDREN")
#XmlElement(name="CHILD_TYPE_A")
List<Long> aChildIds}
#XmlElementWrapper(name = "B_CHILDREN")
#XmlElement(name="CHILD_TYPE_B")
List<Long> bChildIds}
#XmlElementWrapper(name = "C_CHILDREN")
#XmlElement(name="CHILD_TYPE_C")
List<Long> cChildIds}
}
#XmlRootElement(name = "CHILD_TYPE_A")
public class ChildTypeA extends MyAbstractBean {
#XmlElement(name = "ID") private String aId;
}
#XmlRootElement(name = "CHILD_TYPE_B")
public class ChildTypeB extends MyAbstractBean {
#XmlElement(name = "ID") private String bId;
}
#XmlRootElement(name = "CHILD_TYPE_C")
public class ChildTypeC extends MyAbstractBean {
#XmlElement(name = "ID") private String cId;
}
For each container bean passed to the writer, I need to create a unique XML file for each RecordBean e.g. record_1.xml, and I need to write each collection into an aggregate file that will serve as a library of all children for that child type, across all the records.
Output example:
record_1.xml
<?xml version="1.0" encoding="UTF-8"?>
<RECORD>
<ID>1</ID>
<A_CHILDREN>
<CHILD_TYPE_A>1</CHILD_TYPE_A>
<CHILD_TYPE_A>2</CHILD_TYPE_A>
</A_CHILDREN>
<B_CHILDREN>
<CHILD_TYPE_B>1</CHILD_TYPE_B>
<CHILD_TYPE_B>2</CHILD_TYPE_B>
</B_CHILDREN>
<A_CHILDREN>
<CHILD_TYPE_C>1</CHILD_TYPE_C>
<CHILD_TYPE_C>2</CHILD_TYPE_C>
</A_CHILDREN>
</RECORD>
</xml>
record_2.xml
<?xml version="1.0" encoding="UTF-8"?>
<RECORD>
<ID>2</ID>
<A_CHILDREN>
<CHILD_TYPE_A>3</CHILD_TYPE_A>
<CHILD_TYPE_A>4</CHILD_TYPE_A>
</A_CHILDREN>
<B_CHILDREN>
<CHILD_TYPE_B>3</CHILD_TYPE_B>
<CHILD_TYPE_B>4</CHILD_TYPE_B>
</B_CHILDREN>
<A_CHILDREN>
<CHILD_TYPE_C>3</CHILD_TYPE_C>
<CHILD_TYPE_C>4</CHILD_TYPE_C>
</A_CHILDREN>
</RECORD>
</xml>
a_children.xml
<?xml version="1.0" encoding="UTF-8"?>
<A_CHILDREN>
<CHILD_TYPE_A>
<ID>1</ID>
</CHILD_TYPE_A>
<CHILD_TYPE_A>
<ID>2</ID>
</CHILD_TYPE_A>
<CHILD_TYPE_A>
<ID>3</ID>
</CHILD_TYPE_A>
<CHILD_TYPE_A>
<ID>4</ID>
</CHILD_TYPE_A>
</A_CHILDREN>
</xml>
<!-- Decide which format to use -->
b_children.xml & c_children.xml are the same as a_children.xml.
So after the better part of two days, I decided to take a different approach. I think it would be possible to create a set of writers to handle the task, but I think it would require a fair amount of customization, and a complex hierarchy of delegation.
For example:
CompositeItemWriter delegates to:
ClassifierCompositeItemWriter classifies, and delegates to:
StaxEventItemWriter for ChildTypeA
StaxEventItemWriter for ChildTypeB
StaxEventItemWriter for ChildTypeC
MultiResourceItemWriter Dynamically assigns FilesystemResource for each RecordBean.
Despite the fact that it probably could be done, I realized that it's just simpler to write the XML as strings wherever I want. Something along the lines of:
#Component
#Scope("step")
public class MyXmlWriter implements ItemWriter<MyBean> {
#Override
public void write(List<? extends MyBean> beans) throws Exception {
for(MyBean bean : beans) {
this.writeRecordBean(bean.getRecordBean());
bean.getAChildIds().forEach(a -> this.writeElement(a, pathA));
bean.getBChildIds().forEach(b -> this.writeElement(b, pathB));
bean.getCChildIds().forEach(c -> this.writeElement(c, pathC));
}
}
private void writeElement(Long Id, Path path) {
// Handle serialization and FileIO here
}
.
.
.
}
Anyway, I hope this helps anyone digging into the Spring Batch weeds like I was. Cheers!

jackson XML serialization: list of inherited classes

I want to serialize/deserialize such XML:
<Multi>
<child-001>
<name/>
<value/>
</child-001>
<child-002>
<name/>
<value/>
</child-002>
</Multi>
where the child-001 and child-002 are classes inherited from the same parent.
public abstract class Parent {
private String name;
}
#JacksonXmlRootElement(localName = "child-001")
public class Child001 extends Parent {
private String value;
}
#JacksonXmlRootElement(localName = "child-002")
public class Child002 extends Parent {
private String value;
}
The encapsulating class looks like this:
class Multi {
#JacksonXmlElementWrapper(useWrapping = false)
private List<Parent> nodes = new ArrayList<>();
}
Without #JacksonXmlElementWrapper(useWrapping = false) I've got:
<Multi>
<nodes>
<nodes>
<name>name001</name>
<value>value001</value>
</nodes>
<nodes>
<name>name002</name>
<value>value002</value>
</nodes>
</nodes>
</Multi>
With the annotation I've got:
<Multi>
<nodes>
<name>name001</name>
<value>value001</value>
</nodes>
<nodes>
<name>name002</name>
<value>value002</value>
</nodes>
</Multi>
This is quite close to what I need, but still need to replace "nodes" with "child-001" and "child-002".
Can someone point me where to find a solution? Or should I use JAXB instead of Jackson?
Thanks
Normally to represent exaclty the example XML file, Multi class should look like this and this works well:
class Multi {
#JacksonXmlProperty(localName = "child-001")
private Child001 child001;
#JacksonXmlProperty(localName = "child-002")
private Child002 child002;
}
In your code you are using a List of objects of parent class instead, presumably because you may have any number of elements deserialized into subclasses of Parent ? That's a case of polymorphic deserialization that requires defining type info e.g.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(name = "child-001", value = Child001.class),
#JsonSubTypes.Type(name = "child-002", value = Child002.class)
})
abstract class Parent {
private String name;
}
Unfortunately, due to an as yet (jackson 2.9.2) unresolved issue Jackson expects a wrapper around each element e.g.
<Multi>
<nodes>
<child-001>
<name>n1</name>
<value>v1</value>
</child-001>
</nodes>
<nodes>
<child-002>
<name>n2</name>
<value>v2</value>
</child-002>
</nodes>
</Multi>
Until this issue is resolved consider if the first option. If there are fields you don't want serialized leave them null.

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.

JAXB-RI Marshalling generic list gives incorrect output

We have a complicated structure of classes that we are trying to marshal to an xml file using JAXB-RI. The marshalling seems to work correctly with Spring's jaxb2Marshaller but not with the jaxb-ri, which is what we are trying to use. (We're using Java 6 and jaxb-2.1.13)
This is an example of the output we currently see after marshalling with JAXB-RI:
<?xml version="1.0" encoding="UTF-8"?>
<specificCompanyList>
<org>com.ourcompany.etc.etc.TypeOfCompany#56cb0eed</org>
<org>com.ourcompany.etc.etc.TypeOfCompany#3125a57</org>
....
</specificCompanyList>
This is what we want to see:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<specificCompanyList>
<org id="123456" name=" COMPANY NAME 1"/>
<org id="098765" name=" COMPANY NAME 2"/>
....
</specificCompanyList>
Here is some info about our class structure. Apologies if it's confusing -- I want to lay everything out on the table. Class names and paths have been changed and shortened as well.
Class that becomes the root element:
#XmlRootElement(name="specificCompanyList")
public class SpecificCompanyLookup extends BaseCompanyLookup<TypeOfCompany> {
public SpecificCompanyLookup() {}
....
}
BaseCompanyLookup:
#XmlAccessorType(XmlAccessType.FIELD)
abstract class BaseCompanyLookup<T extends OrgNode> implements Lookup {
#XmlElement(name="org")
final Set<T> companyList = new TreeSet<T>(.....);
}
OrgNode:
#XmlAccessorType(XmlAccessType.FIELD)
classOrgNode extends BaseLookupItem {
}
BaseLookupItem:
#XmlAccessorType(XmlAccessType.FIELD)
public class BaseLookupItem {
#XmlAttribute(required = true)
protected String id;
#XmlAttribute(required = true)
protected String name;
....
}
A class that extends OrgNode:
class TypeOfCompany extends OrgNode {
....
}
So:
BaseLookupItem -> OrgNode -> TypeOfCompany
Does anyone know what is causing this bad output? How do we make the JAXB-RI marshaller generate the output that we need?
EDIT: We found the solution. This happened when we moved to WebLogic 12c, which has switched its JAXB default to the EclipseLink MOXy implementation. That implementation appears to have a bug for the case I described here. Following the instructions to switch to the Glassfish JAXB RI fixed this issue for us. Here are those instructions: http://docs.oracle.com/cd/E24329_01/web.1211/e24964/data_types.htm#CIHBHDGI
I confirmed this is in fact a bug in MOXy and have opened the following bug to track the issue https://bugs.eclipse.org/bugs/show_bug.cgi?id=410001
I also confirmed that adding the #XmlSeeAlso annotation like this would be a workaround for this case.
#XmlSeeAlso(TypeOfCompany.class)
public class SpecificCompanyLookup extends BaseCompanyLookup<TypeOfCompany> {
public SpecificCompanyLookup() {}
}

Categories