Let me know the best way to serialize my Java object Download. This is a class generated from a java wsimport tool from a WSDL.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Download", propOrder = {
"Response",
"VendorInformation",
"DownloadItem",
"DownloadCommentItem",
"DownloadIntercomItem"
})
public class Download
{
#XmlElement(name = "Response")
protected ResponseMessageManagementType Response;
#XmlElement(name = "VendorInformation")
protected DownloadVendorInformation VendorInformation;
#XmlElement(name = "DownloadItem")
protected List<DownloadDownloadItem> DownloadItem;
#XmlElement(name = "DownloadCommentItem")
protected ArrayOfDownloadDldComment DownloadCommentItem;
#XmlElement(name = "DownloadIntercomItem")
protected ArrayOfDownloadDldIntercom DownloadIntercomItem;
.........................
}
The java classes generated from the tool do not have any serlization implementation.
And I want to serialize the Download class following this kind of format:
<?xml version="1.0" encoding="utf-8"?>
<Download xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd">
<Response>
.....
</Response>
<VendorInformation>
...............
</VendorInformation>
<DownloadItem>
<DownloadDownloadItem>
.......
</DownloadDownloadItem>
<DownloadDownloadItem>
.......
</DownloadDownloadItem>
<DownloadDownloadItem>
.......
</DownloadDownloadItem>
</DownloadItem>
<DownloadCommentItem>
........
</DownloadCommentItem>
<DownloadIntercomItem>
........
</DownloadIntercomItem>
</Download>
You can see the mapping between XmlElementName and the content of the XML string.
I am at loss on how to do this.
Thanks
This is JAXB. You would need:
JAXBContext ctx = JAXBConetxt.newInstance(Download.class);
Marshaller m = ctx.createMarshaller();
m.marshal(downloadObject, out);
where out can be lots of things, including OutputStream, Writer and File. If you want to get it as a String, use a StringWriter
This is JAXB, and to get your example working you need to supply root element and namespace information:
Root Element
When you marshal an object with JAXB it requires information about the root element. One way to do this is to annotate your Download class with #XmlRootElement
#XmlRootElement(name="Download")
public class Download
If you cannot do that you will need to wrap your instance of Download in a JAXBElement:
Download download = new Download();
QName qname = new QName("HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd";
JAXBElement<Download> jaxbElement = new JAXBElement(qname, "Download"), Download.class, download);
Namespace Qualification
Also to get the namespace qualification you are after you can use the package level #XmlSchema annotation:
#XmlSchema(
namespace="HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd",
elementFormDefault=XmlNsForm.QUALIFIED)
package your.model.package.containing.download;
import javax.xml.bind.annotation.*;
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Download.class);
Download download = new Download();
QName qname = new QName("HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd";
JAXBElement<Download> jaxbElement = new JAXBElement(qname, "Download"), Download.class, download);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbElement, System.out);
}
}
Related
I have a situation here, trying to act as a gateway between two APIs. What I need to do, is:
make a request to an APIa;
parse (marshal) the XML response into an java object;
make little changes to it;
and then give a response in XML (unmarshal) to the other end (APIb).
The thing is that I use the same object to parse the API response and to send the response to the other end.
public class ResponseAPI{
#XmlElement(name="ResponseCode") //I receive <ResponseCode> but I need to send <ResultCode>
private String responseCode;
//getter and setter
}
as the comment says: I receive but I need to send
Is there a way to get this done without having to create another extra class which carries ResultCode?
thanks in advance!
You can try next solution using #XmlElements annotaion
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI
{
#XmlElements(
{
#XmlElement(name = "ResponseCode"),
#XmlElement(name = "ResultCode")
})
private String responseCode;
// ...
}
In this case both ResponseCode and ResultCode will be used during unmarshalling (xml -> object) and only ResultCode during marshalling (object -> xml).
So you can unmarshall XML like
<responseAPI>
<ResponseCode>404</ResponseCode>
</responseAPI>
After marshalling object will looks like
<responseAPI>
<ResultCode>404</ResultCode>
</responseAPI>
Note:
The answer given by Ilya works but isn't guaranteed to work across all implementations of JAXB or even across versions of a single JAXB implementation. The #XmlElements annotation is useful when the decision of which element to marshal depends on the type of the value (see: http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html). In your use case both the ResponseCode and ResultCode elements correspond to type String, unmarshalling will always work fine, but the choice of which element to output is arbitrary. Some JAXB Impls may have last specified wins, but others could easily have first wins.
You could do the following by leveraging #XmlElementRef.
Java Model
ResponseAPI
We will change the responseCode property from type String to JAXBElement<String>. The JAXBElement allows us to store the element name as well as the value.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI{
#XmlElementRefs({
#XmlElementRef(name = "ResponseCode"),
#XmlElementRef(name = "ResultCode")
})
private JAXBElement<String> responseCode;
public JAXBElement<String> getResponseCode() {
return responseCode;
}
public void setResponseCode(JAXBElement<String> responseCode) {
this.responseCode = responseCode;
}
}
ObjectFactory
The #XmlElementRef annotations we used on the ResponseAPI class correspond to #XmlElementDecl annotations on a class annotated with #XmlRegistry. Traditionally this class is called ObjectFactory but you can call it anything you want.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="ResponseCode")
public JAXBElement<String> createResponseCode(String string) {
return new JAXBElement<String>(new QName("ResponseCode"), String.class, string);
}
#XmlElementDecl(name="ResultCode")
public JAXBElement<String> createResultCode(String string) {
return new JAXBElement<String>(new QName("ResultCode"), String.class, string);
}
}
Demo Code
input.xml
<responseAPI>
<ResponseCode>ABC</ResponseCode>
</responseAPI>
Demo
When creating the JAXBContext we need to ensure that we include the class that contains the #XmlElementDecl annotations.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ResponseAPI.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("Scratch/src2/forum24554789/input.xml");
ResponseAPI responseAPI = (ResponseAPI) unmarshaller.unmarshal(xml);
ObjectFactory objectFactory = new ObjectFactory();
String responseCode = responseAPI.getResponseCode().getValue();
JAXBElement<String> resultCodeJAXBElement = objectFactory.createResultCode(responseCode);
responseAPI.setResponseCode(resultCodeJAXBElement);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(responseAPI, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<responseAPI>
<ResultCode>ABC</ResultCode>
</responseAPI>
So, lets say I have this xml with several namespaces.
<Envelope xmlns:pdi="http://www.mypage.com/schemas/pdi" xmlns:ib="http://www.mypage.com/schemas/ib" xmlns="http://www.mypage.com/schemas/envelope">
<Product>
<pdi:number>123456</pdi:number>
</Product>
<Instance>
<ib:serial>abcdefg</ib:serial>
</Instance>
</Envelope>
I'm trying to build a client for it. I have an Envelope POJO that's declared like this
#XmlRootElement(name ="Envelope", namespace = "http://www.mypage.com/schemas/envelope")
public class Envelope
and inside, it has these attributes
#XmlElement(name="Product", namespace = "http://www.mypage.com/schemas/pdi")
public Product getProduct(){...}
#XmlElement(name="Instance", namespace = "http://www.mypage.com/schemas/ib")
public Instance getInstance(){...}
Also, the Product POJO looks like this:
#XmlRootElement(name="Product", namespace = "http://www.mypage.com/schemas/pdi")
public class Product
and attribute
#XmlElement(name="pdi:number", namespace = "http://www.mypage.com/schemas/pdi")
public int getNumber(){...}
For some reason, I can't get the product number. I keep getting a request error. Am I handling the namespaces correctly, or am I missing something?
For this use case I would recommend leveraging the package level #XmlSchema annotation to specify the namespace qualification.
package-info (forum14651918/package-info.java)
#XmlSchema(
namespace="http://www.mypage.com/schemas/envelope",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(namespaceURI = "http://www.mypage.com/schemas/envelope", prefix = ""),
#XmlNs(namespaceURI = "http://www.mypage.com/schemas/ib", prefix = "ib"),
#XmlNs(namespaceURI = "http://www.mypage.com/schemas/pdi", prefix = "pdi")
}
)
#XmlAccessorType(XmlAccessType.FIELD)
package forum14651918;
import javax.xml.bind.annotation.*;
Envelope (forum14651918/Envelope.java)
Since we have specified a namespace and elementFormDefault on the #XmlSchema annotation, all the elements corresponding to the Envelope class will be automatically qualified with the http://www.mypage.com/schemas/envelope namespace.
package forum14651918;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Envelope")
public class Envelope {
#XmlElement(name="Product")
private Product product;
#XmlElement(name="Instance")
private Instance instance;
}
Product (forum14651918/Product.java)
You can override the namespace for the Product class using the #XmlType annotation.
package forum14651918;
import javax.xml.bind.annotation.*;
#XmlType(namespace="http://www.mypage.com/schemas/pdi")
public class Product {
private int number;
}
Instance (forum14651918/Instance.java)
You can override the namespace for the Instance class using the #XmlType annotation.
package forum14651918;
import javax.xml.bind.annotation.XmlType;
#XmlType(namespace="http://www.mypage.com/schemas/ib")
public class Instance {
private String serial;
}
Demo (forum14651918/Demo.java)
Below is some code you can run to prove that everything works.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Envelope.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14651918/input.xml");
Envelope envelope = (Envelope) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(envelope, System.out);
}
}
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Try replacing name="pdi:number", namespace = "http://www.mypage.com/schemas/pdi" with name="number", namespace = "http://www.mypage.com/schemas/pdi". Prefix is not needed.
What is more looking at the XML it seems that namespace for both Product and Instance is http://www.mypage.com/schemas/envelope.
You should not need #XmlRootElement annotation for Product class. It is not a root element and is already configured on getProduct().
Full configuration that should be OK is:
#XmlRootElement(name ="Envelope", namespace = "http://www.mypage.com/schemas/envelope")
public class Envelope {
#XmlElement(name="Product", namespace = "http://www.mypage.com/schemas/envelope")
public Product getProduct(){...}
#XmlElement(name="Instance", namespace = "http://www.mypage.com/schemas/envelope")
public Instance getInstance(){...}
}
public class Product {
#XmlElement(name="number", namespace = "http://www.mypage.com/schemas/pdi")
public int getNumber(){...}
}
public class Instance {
#XmlElement(name="serial", namespace = "http://www.mypage.com/schemas/ib")
public String getSerial(){...}
}
I have a schema that defines the following type:
<xsd:complexType name="Payload">
<xsd:sequence>
<xsd:any namespace="##any" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
</xsd:sequence>
</xsd:complexType>
And that creates an object like so:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Payload", propOrder = {
"any"
})
public class Payload {
#XmlAnyElement(lax = true)
protected List<Object> any;
}
Now I try adding another generated JAXB object to that Payload doing something like this:
Class payloadClass = ...;
JAXBContext context = JAXBContext.newInstance( WrapperRequest.class, payloadClass);
...
marshaller.marshal( wrappedRequest );
But I get a terrible exception that looks like it'll never work so I decide to serialize the payload object to XML first then add that as a string in the payload.
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance( sdoRequest.getClass() );
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(new JAXBElement(new QName("uri", sdoRequest.getClass().getSimpleName()), sdoRequest.getClass(), sdoRequest), writer);
payload.getAny().add( writer.toString() );
And this blows up with an exception saying "java.lang.String" does not contain an #XmlRootElement.
So how will the use of xs:any ever work with JAXB? Nothing seems to want to work because JAXB turns the Payload into Object, and it won't serialize just anything in Object. This is all inside Axis2 as well so it's been very challenging to get to this point.
Below I will demonstrate JAXB (JSR-222) and any with an example:
Payload
The any property is annotated with #XmlAnyElement(lax=true). This means that for that property if an element is associated with a class via #XmlRootElement or #XmlElementDecl then an instance of the corresponding object will be used to populate the property if not the element will be set as an instance of org.w3c.dom.Element.
package forum13941747;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Payload", propOrder = {
"any"
})
public class Payload {
#XmlAnyElement(lax = true)
protected List<Object> any;
}
Foo
Below is an example of a class annotated with #XmlRootElement.
package forum13941747;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Foo {
}
Bar
Below is an example of a class without the #XmlRootElement annotation. In this use case we will leverage the #XmlElementDecl annotation on a factory class (usually called ObjectFactory) annotated with #XmlRegistry.
package forum13941747;
public class Bar {
}
ObjectFactory
Below is an example of specifying an #XmlElementDecl annotation for the Bar class.
package forum13941747;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="bar")
public JAXBElement<Bar> createBar(Bar bar) {
return new JAXBElement<Bar>(new QName("bar"), Bar.class, bar);
}
}
input.xml
Below is the input document we'll use for this example. There are 3 elements that correspond to the any property. The first corresponds to the #XmlRootElement annotation on the Foo class. The second corresponds to the #XmlElementDecl annotation for the Bar class and the third does not correspond to any of the domain classes.
<?xml version="1.0" encoding="UTF-8"?>
<payload>
<foo/>
<bar/>
<other/>
</payload>
Demo
In the demo code below we will unmarshal the input document, then output the classes of the objects in the resulting any property and then marshal the payload object back to XML.
package forum13941747;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Payload.class, Foo.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum13941747/input.xml");
Payload payload = (Payload) unmarshaller.unmarshal(xml);
for(Object o : payload.any) {
System.out.println(o.getClass());
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(payload, System.out);
}
}
Output
Below is the output from running the demo code. Note the classes corresponding to the objects in the any property. The foo element became an instance of the Foo class. The bar element became an instance of JAXBElement that holds an instance of Bar. The other element became an instance of org.w3c.dom.Element.
class forum13941747.Foo
class javax.xml.bind.JAXBElement
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<payload>
<foo/>
<bar/>
<other/>
</payload>
Make use of Object Factory for mashelling the object like below you no need to have #XmlRootElement in DemoType.java .,
DemoType demoServiceRequest = new DemoType();
ObjectFactory obDemo = new ObjectFactory();
Request requestObject = new Request();
requestObject.setAny(obDemo.createDemo(demoServiceRequest));
And add DemoType class at Request.java like #XmlSeeAlso({DemoType.class})
Should your payload be a XML string, I managed to solve the very same problem using the code below:
import javax.xml.parsers.DocumentBuilderFactory;
//...
String XMLPAYLOAD = "...";
Payload payload = new ObjectFactory().createPayload();
try {
payload.setAny(DocumentBuilderFactory
.newInstance()
.newDocumentBuilder()
.parse(new InputSource(new StringReader(XMLPAYLOAD)))
.getDocumentElement());
} catch (Exception e) {
e.printStackTrace();
}
//...
Using JAXB I would like to have the possibility to marshal empty lists as absent nodes. I think that EclipseLink MOXy has that possibility, but I can't get it to work.
According to: http://wiki.eclipse.org/User:Rick.barkhouse.oracle.com/Test1 you should be able to do it like this:
#XmlElementWrapper(name="line-items", nillable=true)
#XmlNullPolicy(shouldMarshalEmptyCollections=false)
List<LineItem> item = null;
But
shouldMarshalEmptyCollections
is not a valid property.
I've tried using eclipselink 2.4.0, 2.4.1 and 2.5.0-M4. What am I doing wrong?
You could use EclipseLink JAXB (MOXy)'s #XmlPath mapping to map this use case. I'll demonstrate with an example below how it compares to using #XmlElementWrapper.
Root
package forum13268598;
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementWrapper(name="line-items-element-wrapper")
List<LineItem> item1 = null;
#XmlPath("line-items-xml-path/item1")
List<LineItem> item2 = null;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum13268598;
import java.util.ArrayList;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.item1 = new ArrayList<LineItem>();
root.item2 = new ArrayList<LineItem>();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
In the #XmlElementWrapper use case an element is written out for an empty collection, but it is not for the #XmlPath use case.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<line-items-element-wrapper/>
</root>
I'm trying to unmarshall some dozer mapping files in order to provide a mapping availability library to a number of applications. But i cant get the JaxB annotations to work correctly. Either the list of mappings us unmarshalled as null or empty
From the mapping file, all i'm interested in is.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<mappings>
<mapping>
<class-a>package.MySourceClass</class-a>
<class-b>other.package.DestinationClass</class-b>
</mapping>
</mappings>
I have a mappings class
#XmlRootElement(name="mappings")
#XmlAccessorType(XmlAccessType.FIELD)
public class Mappings {
#XmlElementWrapper(name="mappings")
private List<Mapping> mappingEntries = null;
//Getters and setters omitted
and A mapping class
#XmlRootElement(name="mapping")
#XmlAccessorType(XmlAccessType.FIELD)
public class Mapping {
#XmlElement(name ="class-a")
private String classA;
#XmlElement(name = "class-b")
private String classB;
I've tried numerous combinations of the annotations and I cant figure out what i'm doing wrong.
Can someone point me in the right direction.
You could do the following:
Mappings
package forum11193953;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="mappings") // Match the root element "mappings"
#XmlAccessorType(XmlAccessType.FIELD)
public class Mappings {
#XmlElement(name="mapping") // There will be a "mapping" element for each item.
private List<Mapping> mappingEntries = null;
}
Mapping
package forum11193953;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Mapping {
#XmlElement(name ="class-a")
private String classA;
#XmlElement(name = "class-b")
private String classB;
}
Demo
package forum11193953;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Mappings.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml= new File("src/forum11193953/input.xml");
Mappings mappings = (Mappings) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(mappings, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappings>
<mapping>
<class-a>package.MySourceClass</class-a>
<class-b>other.package.DestinationClass</class-b>
</mapping>
</mappings>
Try the JMapper Framework: http://code.google.com/p/jmapper-framework/
With JMapper you have all the advantages of dynamic mapping with the performance of static code and much more.