jackson fasterxml add namespaces to root element - java

If I have the class A.java:
#JacksonXmlRootElement(localName = "A")
public class A {
}
The output that gets produced is:
<A
xmlns="">
I want to add a few more namespaces to the output, i.e.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com example.xsd"
How do I configure A.java to contain more custom namespaces like these?

Since xsi:schemaLocation is an attribute, you can add it like that:
public class A implements Serializable {
#JacksonXmlProperty(isAttribute = true, localName = "xsi:schemaLocation")
private String schemaLocation = "urn:path:to.your.schema";
It did the job for me.

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.

Java, SimpleXML with different prefixes in one Path

I have a XML file which has that kind of structure:
<a:root>
<a:body>
<b:do_action>
<b:do_input>
<request>
<!-- There are a lot of primitive elements -->
</request>
</b:do_input>
</b:do_action>
</a:body>
</a:root>
I'm trying to parse this XML by using SimpleXML:
public class Request {
// There are a lot of defined primitive elements
}
#Root(name = "root")
#Namespace(prefix = "a")
public class Root {
#Path("a:body/b:do_action/b:do_input")
#Element(name = "request")
public Request request;
}
When I instantiate my object and want to show it as string, I get this error message:
org.simpleframework.xml.core.ElementException: Namespace prefix 'b' in class Request is not in scope
How to deal with paths, which have different prefixes?
Since you use two different namespaces, you should declare both of them:
#Root(name = "root")
#NamespaceList({
#Namespace(prefix = "a" , reference="ref_a"),
#Namespace(prefix = "b", reference="ref_b")})
public class Root {
}
Hope it helps.

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() {}
}

Jaxb: Unmarshalling xml with multiple namespaces in same package

I'm new to using namespaces in xml so I am kind of confused and would like some clarification. I have a java service where I am receiving xml documents with many different namespaces and while i got it working, I feel like I must have done something wrong so I want to check. In my package-info.java I have my schema annotation such as:
#javax.xml.bind.annotation.XmlSchema(
xmlns={
#javax.xml.bind.annotation.XmHS(prefix="train", namespaceURI="http://mycompany/train"),
#javax.xml.bind.annotation.XmHS(prefix="passenger", namespaceURI="http://mycompany/passenger")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm=QUALIFIED
)
I have a Train.java annotated on the class level with:
#XmlRootElement(name="Train", namespace="http://mycompany/train")
and each field in the class annotated with:
#XmlElement(name="Color") for example
Train contains a List of Passenger(s) so there's a property
private Set<Passenger> passengers;
and this collection is annotated with:
#XmlElementWrapper(name="Passengers")
#XmlElements(#XmlElement(name="Passenger", namespace="http://mycompany/passenger"))
Then within Passenger.java the class itself is annotated with:
#XmlElement(name="Passenger", namespace="http://mycompany/passenger")
Finally for individual fields within Passenger.java, they are annotated like this:
#XmlElement(name="TicketNumber", namespace="http://mycompany/passenger")
So when I have an xml that looks like:
<train:Train>
<train:Color>Red</train:Color>
<train:Passengers>
<train:Passenger>
<passenger:TicketNumber>T101</passenger:TicketNumber>
</train:Passenger>
</train:Passengers>
</train:Train>
Now I unmarshal this xml I received and Train's Color property is set and Passenger's TicketNumber property is set. But I don't know why I need to add the namespace url on the XmlElement annotation on TicketNumber for that to work but I didn't need to do so for the Color property on Train. If I remove the namespace attribute from the XmlElement annotation on TicketNumber, the value from the xml wont get mapped to the object unless I also remove the namespace prefix from the xml request. I feel like since I've got the namespace attribute defined on the XmlRootElement for Passenger, I shouldn't need to do that for every single field in the class as well just like I didn't have to for Train so I am assuming I must have setup something wrong. Can someone point me in the right direction? Thanks!
Below is an explanation of how namespaces work in JAXB (JSR-222) based on your model.
JAVA MODEL
package-info
Below is a modified version of your #XmlSchema annotation. It contains some key information:
namespace - The default namespace that will be used to qualify global elements (those corresponding to #XmlRootElement and #XmlElementDecl annotations (and local elements based on the elementFormDefault value) that don't have another namespace specified.
elementFormDefault by default only global elements are namespace qualified but by setting the value to be XmlNsForm.QUALIFIED all elements without an explicit namespace specified will be qualified with the namespace value.
xmlns is the preferred set of prefixes that a JAXB impl should use for those namespaces (although they may use other prefixes).
#XmlSchema(
namespace="http://mycompany/train",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix="train", namespaceURI="http://mycompany/train"),
#XmlNs(prefix="passenger", namespaceURI="http://mycompany/passenger")
}
)
package forum15772478;
import javax.xml.bind.annotation.*;
Train
Since all the elements corresponding to the Train class correspond to the namespace specified on the #XmlSchema annotation, we don't need to specify any namespace info.
Global Elements - The #XmlRootElement annotation corresponds to a global element.
Local Elements - The #XmlElementWrapper and #XmlElement annotations correspond to local elements.
package forum15772478;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Train")
public class Train {
private List<Passenger> passengers;
#XmlElementWrapper(name="Passengers")
#XmlElement(name="Passenger")
public List<Passenger> getPassengers() {
return passengers;
}
public void setPassengers(List<Passenger> passengers) {
this.passengers = passengers;
}
}
Passenger
If all the elements corresponding to properties on the Passenger class will be in the http://mycompany/passenger namespace, then you can use the #XmlType annotation to override the namespace from the #XmlSchema annotation.
package forum15772478;
import javax.xml.bind.annotation.*;
#XmlType(namespace="http://mycompany/passenger")
public class Passenger {
private String ticketNumber;
#XmlElement(name="TicketNumber")
public String getTicketNumber() {
return ticketNumber;
}
public void setTicketNumber(String ticketNumber) {
this.ticketNumber = ticketNumber;
}
}
Alternatively you can override the namespace at the property level.
package forum15772478;
import javax.xml.bind.annotation.*;
public class Passenger {
private String ticketNumber;
#XmlElement(
namespace="http://mycompany/passenger",
name="TicketNumber")
public String getTicketNumber() {
return ticketNumber;
}
public void setTicketNumber(String ticketNumber) {
this.ticketNumber = ticketNumber;
}
}
DEMO CODE
The following demo code can be run to prove that everything works:
Demo
package forum15772478;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Train.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15772478/input.xml");
Train train = (Train) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(train, System.out);
}
}
input.xml/Output
In the XML below I have added the necessary namespace declarations that were missing from the XML document in your question.
<train:Train
xmlns:train="http://mycompany/train"
xmlns:passenger="http://mycompany/passenger">
<train:Color>Red</train:Color>
<train:Passengers>
<train:Passenger>
<passenger:TicketNumber>T101</passenger:TicketNumber>
</train:Passenger>
</train:Passengers>
</train:Train>
FOR MORE INFORMATION
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Categories