Multiple XMLRootElement after JAXB Marshalling - jaxb

I have the following classes with jax-ws webservice;
Interface Class (myserviceInt.java);
#WebService(name="myservice")
#SOAPBinding(style=Style.RPC)
public interface myserviceInt {
#WebMethod(action="urn:OutIn", operationName="ACCT")
public String getACCT(
#WebParam(partName="Prod1")String prod1,
#WebParam(partName="Prod2")String prod2);
}
Implementation Class (myserviceImpl.java);
#WebService(endpointInterface = "path.to.webservice.myserviceInt", portName="acct", serviceName="acctservice", targetNamespace="http://demo.acctservice.com/")
public class myserviceImpl implements myserviceInt {
#Override
public String getACCT(String prod1, String prod1){
Other codes
String id = "25";
String description = "The Third Product"
ACCTResponse acct = new ACCTResponse();
acct.setId(id);
acct.setDescription(description);
String XMLstring = acct.ACCTResponseBeanToXML(acct);
return XMLstring;
}
}
ACCTResponse Class (ACCTResponse.java);
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name="ACCTResponse")
public class ACCTResponse {
#XmlElement(name = "ID")
private String id;
#XmlElement(name = "Description")
private String description;
... their setters and getters;
}
The Marshaller Class (ACCTUtil.java);
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import path.to.responseclass.ACCTResponse;
public class ACCTUtil {
public static String ACCTResponseBeanToXML(ACCTResponse acct)
{
String responseStr = null;
try
{
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(ACCTResponse.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(acct, writer);
responseStr = writer.toString();
int xmlHeaderIndex = responseStr.indexOf("?>");
if(xmlHeaderIndex > 0)
responseStr = responseStr.substring(xmlHeaderIndex + 2, responseStr.length());
}
catch(Exception ex){
ex.printStackTrace();
}
return responseStr;
}
}
every other libraries needed were left out to save time. I also have the package-info.java in the respective path for the ACCTResponse.java to add the required response namespaceURI and prefix.
Now, I got the following as response after Marshalling as described above;
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:ACCTResponse xmlns:ns2="http://demo.acctservice.com/">
<return><![CDATA[<net:ACCTResponse xmlns:net="http://demo.acctservice.com/">
<net:ID>25</net:ID>
<net:Description>The Third Product</net:Description>
</net:ACCTResponse>]]></return>
</ns2:ACCTResponse>
</S:Body>
</S:Envelope>
The response I want to achieve for a consumer of this webservice that have a fixed client is this;
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org /soap/envelope/">
<saop:Body>
<net:ACCTResponse xmlns:net="http://demo.acctservice.com/">
<net:ID>25</net:ID>
<net:Description>The Third Product</net:Description>
</net:ACCTResponse>
</soapenv:Body>
</soapenv:Envelope>
What modifications/manipulations do I need, to achieve this response from my codes. Thanks.

Related

Creating null object for input StringReader using JAXBContext

I am trying to create bean from string but unable to create as it is returning null.Here is my code
public ModelAndView checkPhotoQualityRequest(
#RequestBody String photoDataXml, HttpServletRequest request) {
PhotoQuality photoQuality = null;
try {
JAXBContext jaxbContext = JAXBContext
.newInstance(PhotoQuality.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(photoDataXml);
photoQuality = (PhotoQuality) unmarshaller.unmarshal(reader);
PhotoQuality.java
package in.gov.uid.opencvaccess.bean;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlType(name = "PhotoQuality")
#XmlRootElement(name = "PhotoQuality")
public class PhotoQuality {
private String photoid;
private byte[] photo;
private boolean quality;
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getPhotoid() {
return photoid;
}
public void setPhotoid(String photoid) {
this.photoid = photoid;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
public boolean isQuality() {
return quality;
}
public void setQuality(boolean quality) {
this.quality = quality;
}
}
Please help me to sort out this issue.When I debug and check bean its showing all null values but photoDataXml showing complete xml.
I have found the reason.When I tried using RESTClient its giving null object. But as soon as I written client code and passed StringWriter value created from PhotoQuality Object then it runs properly.

JAXB Binder.updateXml(objectTobeUpdated) fails when called twice

I have an existing XML, im using Jaxb to update it. Following code fails: there are bugs raised on this issue, but couldnt get any information about the fix. Can anyone kindly help on how to resolve the issue.
package la.te.st;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Student{
String name;
int age;
int id;
public String getName(){
return name;
}
#XmlElement
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
#XmlElement
public void setAge(int age){
this.age = age;
}
public int getId(){
return id;
}
#XmlAttribute
public void setId(int id){
this.id = id;
}
#Override
public String toString() {
return this.name + " age:" + this.age + " id:" + this.id;
}
}
/**************************************/
package la.te.st;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Binder;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class BinderDemo {
public static void main(String[] args) {
try {
// we need a blank document to store final xml output
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbf.newDocumentBuilder();
Document document = docBuilder.parse("Student.xml");
// create JAXBContext which will be used to create a Binder
JAXBContext jc = JAXBContext.newInstance(Student.class);
Binder<Node> binder = jc.createBinder();
// set output as formatted one
binder.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// get xml node from the document
Node xmlNode = document.getDocumentElement();
// Returns the updated JAXB object
Student st = (Student)binder.updateJAXB(xmlNode);
System.out.println(st);
// set age and name
st.setAge(11);
st.setName("Sania");
System.out.println(st);
// update xml node with new data
xmlNode = binder.updateXML(st);
st.setAge(12);
st.setName("Sania");
System.out.println(st);
xmlNode = binder.updateXML(st);
// set node value to the document
document.setNodeValue(xmlNode.getNodeValue());
// finally print the edited object on stdout
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.transform(new DOMSource(document), new StreamResult(System.out));
}catch(Exception ex) {
ex.printStackTrace();
}
}
}
/*****************************/
the Student.xml file :
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<student id="10">
<age>10</age>
<name>Zara Ali</name>
</student>

JAXB use String as it is

I use REST and i was wondering if i can tell jaxb to insert a string field "as-it-is" into the outgoing xml.
Certainly i count unpack it before returning, but i would like to save this step.
#XmlRootElement(name="unnestedResponse")
public class Response{
#Insert annotation here ;-)
private String alreadyXml;
private int otherDate; ...
}
Is there a possability to tell JAXB to just use the String as it is without escapting? I want that the client does not have to parse my response and then parse this field.
greetings,
m
You can use the #XmlAnyElement and specify a DomHandler to keep a portion of the XML document as a String.
Customer
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private String bio;
#XmlAnyElement(BioHandler.class)
public String getBio() {
return bio;
}
public void setBio(String bio) {
this.bio = bio;
}
}
BioHandler
import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;
public class BioHandler implements DomHandler<String, StreamResult> {
private static final String BIO_START_TAG = "<bio>";
private static final String BIO_END_TAG = "</bio>";
private StringWriter xmlWriter = new StringWriter();
public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
return new StreamResult(xmlWriter);
}
public String getElement(StreamResult rt) {
String xml = rt.getWriter().toString();
int beginIndex = xml.indexOf(BIO_START_TAG) + BIO_START_TAG.length();
int endIndex = xml.indexOf(BIO_END_TAG);
return xml.substring(beginIndex, endIndex);
}
public Source marshal(String n, ValidationEventHandler errorHandler) {
try {
String xml = BIO_START_TAG + n.trim() + BIO_END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
For More Information
http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
Following bdoughan's answer did not work for me as I encountered errors during marshalling when the text contained the '& character (e.g. in URLs or when using HTML entities such as e.g. " ").
I was able to resolve this by changing the custom DomHandler's marshal method to
public Source marshal(String et, ValidationEventHandler veh) {
Node node = new SimpleTextNode(et);
return new DOMSource(node);
}
where SimpleTextNode implements the Node interface as follows:
class SimpleTextNode implements Node {
String nodeValue = "";
#Override
public SimpleTextNode(String nodeValue) {
this.nodeValue = nodeValue;
}
#Override
public short getNodeType() {
return TEXT_NODE;
}
// the remaining methods of the Node interface are not needed during marshalling
// you can just use the code template of your IDE...
...
}
PS: I would have loved to leave this as a comment to bdoughan's answer, but unfortunately I have way too little reputation :-(

MOXy JAXB: how to map several XML tag elements to the same JAVA bean property

I am trying to unmarshall an XML file using MOXy JAXB. I have a set of classes, already generated, and I am using Xpath to map every XML element I need into my model.
I have an XML file like this:
<?xml version="1.0" encoding="UTF-8"?>
<fe:Facturae xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae">
<Parties>
<SellerParty>
<LegalEntity>
<CorporateName>Company Comp SA</CorporateName>
<TradeName>Comp</TradeName>
<ContactDetails>
<Telephone>917776665</Telephone>
<TeleFax>917776666</TeleFax>
<WebAddress>www.facturae.es</WebAddress>
<ElectronicMail>facturae#mityc.es</ElectronicMail>
<ContactPersons>Fernando</ContactPersons>
<CnoCnae>28000</CnoCnae>
<INETownCode>2134AAB</INETownCode>
<AdditionalContactDetails>Otros datos</AdditionalContactDetails>
</ContactDetails>
</LegalEntity>
</SellerParty>
<BuyerParty>
<Individual>
<Name>Juana</Name>
<FirstSurname>Mauriño</FirstSurname>
<OverseasAddress>
<Address>Juncal 1315</Address>
<PostCodeAndTown>00000 Buenos Aires</PostCodeAndTown>
<Province>Capital Federal</Province>
<CountryCode>ARG</CountryCode>
</OverseasAddress>
<ContactDetails>
<Telephone>00547775554</Telephone>
<TeleFax>00547775555</TeleFax>
</ContactDetails>
</Individual>
</BuyerParty>
</Parties>
</fe:Facturae>
Then I have my model:
#XmlRootElement(namespace="http://www.facturae.es/Facturae/2009/v3.2/Facturae", name="Facturae")
public class Facturae implements BaseObject, SecuredObject, CreationDataAware {
#XmlPath("Parties/SellerParty")
private Party sellerParty;
#XmlPath("Parties/BuyerParty")
private Party buyerParty;
}
public class Party implements BaseObject, SecuredObject, CreationDataAware {
#XmlPath("LegalEntity/ContactDetails")
private ContactDetails contactDetails;
}
As you can see, <ContactDetails></ContactDetails> is present in <SellerParty></SellerParty> and <BuyerParty></BuyerParty> but this two tags share the same JAVA object (Party). With the previous mapping (#XmlPath("LegalEntity/ContactDetails")) I can pass correctly the ContactDetails info in SellerParty, but I want also to pass the ContactDetails in <BuyerParty> at the same time.
I was trying something like that:
#XmlPaths(value = { #XmlPath("LegalEntity/ContactDetails"),#XmlPath("Individual/ContactDetails") })
private ContactDetails contactDetails;
but it doesn't work.
Can you guys give me a hand?
Thank you very much.
You could use an XmlAdapter for this use case:
PartyAdapter
We will use an XmlAdapter to convert an instance of Party to another type of object AdaptedParty. AdaptedParty will have two properties corresponding to each property in Party (one for each mapping possibility). We will take advantage of the ability to pre-initialize an instance of XmlAdapter to set an instance of Facturae on it. We will use the instance of Facturae to determine if the instance of Party we are handling is a sellerParty or a buyerParty.
package forum9807536;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
public class PartyAdapter extends XmlAdapter<PartyAdapter.AdaptedParty, Party> {
private Facturae facturae;
public PartyAdapter() {
}
public PartyAdapter(Facturae facturae) {
this.facturae = facturae;
}
#Override
public Party unmarshal(AdaptedParty v) throws Exception {
Party party = new Party();
if(v.individualName != null) {
party.setName(v.individualName);
party.setContactDetails(v.individualContactDetails);
} else {
party.setName(v.sellPartyName);
party.setContactDetails(v.sellerPartyContactDetails);
}
return party;
}
#Override
public AdaptedParty marshal(Party v) throws Exception {
AdaptedParty adaptedParty = new AdaptedParty();
if(null == facturae || facturae.getSellerParty() == v) {
adaptedParty.sellPartyName = v.getName();
adaptedParty.sellerPartyContactDetails = v.getContactDetails();
} else {
adaptedParty.individualName = v.getName();
adaptedParty.individualContactDetails = v.getContactDetails();
}
return adaptedParty;
}
public static class AdaptedParty {
#XmlPath("Individual/Name/text()")
public String individualName;
#XmlPath("Individual/ContactDetails")
public ContactDetails individualContactDetails;
#XmlPath("LegalEntity/CorporateName/text()")
public String sellPartyName;
#XmlPath("LegalEntity/ContactDetails")
public ContactDetails sellerPartyContactDetails;
}
}
Party
We will use the #XmlJavaTypeAdapter to associate the PartyAdapter to the Party class:
package forum9807536;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlJavaTypeAdapter(PartyAdapter.class)
public class Party implements BaseObject, SecuredObject, CreationDataAware {
private String name;
private ContactDetails contactDetails;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ContactDetails getContactDetails() {
return contactDetails;
}
public void setContactDetails(ContactDetails contactDetails) {
this.contactDetails = contactDetails;
}
}
Demo
The following demo code demonstrates how to set a pre-initialized XmlAdapter on the Marshaller:
package forum9807536;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Facturae.class);
File xml = new File("src/forum9807536/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Facturae facturae = (Facturae) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setAdapter(new PartyAdapter(facturae));
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(facturae, System.out);
}
}
Output
Below is the output from the demo code that corresponds to the portion of your model that I have mapped.
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Facturae xmlns:ns0="http://www.facturae.es/Facturae/2009/v3.2/Facturae">
<Parties>
<BuyerParty>
<Individual>
<Name>Juana</Name>
<ContactDetails/>
</Individual>
</BuyerParty>
<SellerParty>
<LegalEntity>
<CorporateName>Company Comp SA</CorporateName>
<ContactDetails/>
</LegalEntity>
</SellerParty>
</Parties>
</ns0:Facturae>
For More Information
http://blog.bdoughan.com/search/label/XmlAdapter
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

Cannot unmarshall using #XmlAttribute

I can marshall my sitemap with JAXB, though I cannot unmarshall it! Any help would be much appreciated.
I get the following exception:
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset").
Expected elements are <{}urls>,<{}urlset>
Sitemap urlset:
#XmlRootElement(name = "urlset")
public class XMLURLSet
{
List<XMLURL> urls;
final String xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
#XmlAttribute(name = "xmlns")
public String getXmlns() {
return xmlns;
}
public void setXmlns(String xmlns) {
// nop
}
#XmlElement(name = "url")
public List<XMLURL> getUrls(){
return urls;
}
public void setUrls(List<XMLURL> urls) {
this.urls = urls;
}
Sitemap urls:
#XmlRootElement(name = "urls")
public class XMLURL {
String loc;
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
Output XML (it's correct):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.example.com</loc>
</url>
</urlset>
JUnit marshalling code that works
JAXBContext context = JAXBContext.newInstance(XMLURLSet.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter sw = new StringWriter();
marshaller.marshal(urlSet, sw);
String xml = sw.toString();
Unmarshalling that DOESNT WORK (cont' from above):
JAXBContext ctx = JAXBContext.newInstance(XMLURLSet.class);
Unmarshaller umsler = ctx.createUnmarshaller();
XMLURLSet xmlUrlSet = (XMLURLSet) umsler.unmarshal(new StreamSource(new StringReader(xml)));
The last line generates the exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset"). Expected elements are <{}urls>,<{}urlset>
The problem is with the way you are trying to namespace qualify your document. You will need to add a class called package-info in the same package as your domain classes with the following entry:
package-info
#XmlSchema(
namespace = "http://www.sitemaps.org/schemas/sitemap/0.9",
elementFormDefault = XmlNsForm.QUALIFIED)
package your.package.name;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Resources