I've a service that returns this XML:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<result>
<project>
<id>id1</id>
<owner>owner1</owner>
</project>
<project>
<id>id2</id>
<owner>owner2</owner>
</project>
</result>
or
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<result>
<user>
<id>id1</id>
<name>name1</name>
</user>
<user>
<id>id2</id>
<name>name2</name>
</user>
</result>
I want to unmarshall the retrieved XML using these classes:
Result:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {
#XmlElement
protected String status;
#XmlElementWrapper(name = "result")
#XmlElement
protected List<T> result;
}
Project:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Project {
#XmlElement
public String id;
#XmlElement
public String owner;
}
User:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class User {
#XmlElement
public String id;
#XmlElement
public String name;
}
First not working solution
JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StreamSource source = new StreamSource(new File("responseProject.xml"));
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source);
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);
source = new StreamSource(new File("responseUser.xml"));
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source);
System.out.println(responseUser.getStatus());
for (User user:responseUser.getResult()) System.out.println(user);
I get an empty list.
Second not working solution
Inspired by this article http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html I've modified the Response class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {
#XmlElement
protected String status;
#XmlAnyElement(lax=true)
protected List<T> result;
}
And then tested it with this code:
Response<Project> responseProject = unmarshal(unmarshaller, Project.class, "responseProject.xml");
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);
private static <T> Response<T> unmarshal(Unmarshaller unmarshaller, Class<T> clazz, String xmlLocation) throws JAXBException {
StreamSource xml = new StreamSource(xmlLocation);
#SuppressWarnings("unchecked")
Response<T> wrapper = (Response<T>) unmarshaller.unmarshal(xml, Response.class).getValue();
return wrapper;
}
And I get this exception reading the response list:
Exception in thread "main" java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to org.test.Project
Note: I can't modify the original XML. There are more types other than Project and User.
Thanks to Blaise Doughan and his article I've found the solution.
First we need the Wrapper class provided in the article:
#XmlRootElement
public class Wrapper<T> {
private List<T> items;
public Wrapper() {
items = new ArrayList<T>();
}
public Wrapper(List<T> items) {
this.items = items;
}
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Then I've modified the Response class in order to use it:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {
#XmlElement
protected String status;
#XmlElement
protected Wrapper<T> result;
...
public Response(String status, List<T> result) {
this.status = status;
this.result = new Wrapper<>(result);
}
...
public List<T> getResult() {
return result.getItems();
}
...
}
Finally the unmarshalling code:
JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class, Wrapper.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StreamSource source = new StreamSource(new File("responseProject.xml"));
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source);
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);
source = new StreamSource(new File("responseUser.xml"));
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source);
System.out.println(responseUser.getStatus());
for (User user:responseUser.getResult()) System.out.println(user);
I've added the Wrapper class to the context class list.
Alternatively you can add this annotation to the Response class:
#XmlSeeAlso({Project.class, User.class})
Using #XmlSeeAlso({Project.class, User.class}) on Response classes has the drawback of generating some garbage information on each entity in the list: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount"
<resources>
<links>
<link>
<rel>self</rel>
<uri>http://localhost:8080/salonea-1.0/rest/user-accounts?offset=0&limit=2</uri>
</link>
<link>
<rel>prev</rel>
<uri></uri>
</link>
<link>
<rel>next</rel>
<uri>http://localhost:8080/salonea-1.0/rest/user-accounts?offset=2&limit=2</uri>
</link>
</links>
<collection>
<user-account
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount">
<accountType>user</accountType>
<activationCode>638f502a0e409348ccc2e36c24907f0</activationCode>
<email>michzio#hotmail.com</email>
<login>michzio</login>
<password>sAmPL3#e</password>
<registrationDate>2015-09-03T17:30:03+02:00</registrationDate>
<userId>1</userId>
</user-account>
<user-account
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount">
<accountType>user</accountType>
<activationCode>334bc79d142a291894bd71881e38a719</activationCode>
<email>alicja#krainaczarow.com</email>
<login>alicja</login>
<password>zAczka!00</password>
<registrationDate>2015-09-03T17:30:03+02:00</registrationDate>
<userId>2</userId>
</user-account>
</collection>
</resources>
Related
In my spring boot application, I have below DTO class
#Data
public clsss Feed {
private int id;
private String name;
private String title;
#Builder
#XmlRootElement(name = "feeds")
public static class Feeds {
#XmlElement(name = "feed")
#Singular
private List<Feed> feeds;
}
}
My config class as below
#Component
public class JacksonCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.modulesToInstall(new JaxbAnnotationModule());
}
}
DAO class implementation as below
public Feeds getAll() {
String sqlQuery = "SELECT * FROM feed WHERE trash = 0";
return Feeds.builder().feeds(namedParameterJdbcTemplate.query(sqlQuery, new BeanPropertyRowMapper<>(Feed.class))).build();
}
Using my ReST API, XML response I am receiving as below:
<feeds>
<feed>
<feed>
<id>1</id>
<name>Val1</name>
<title>Title1</title>
</feed>
<feed>
<id>2</id>
<name>Val2</name>
<title>Title2</title>
</feed>
</feed>
</feeds>
I want to remove <feed> which comes as a wrapper element. Desired output is as below:
<feeds>
<feed>
<id>1</id>
<name>Val1</name>
<title>Title1</title>
</feed>
<feed>
<id>2</id>
<name>Val2</name>
<title>Title2</title>
</feed>
</feeds>
Make changes in the config class to set the default wrapper to false.
#Component
public class JacksonCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.modulesToInstall(new JaxbAnnotationModule());
jacksonObjectMapperBuilder.defaultUseWrapper(false); //This was missing before
}
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<InstanceTree xmlns="http://www.testsite.org/Schemas/xyzSchema">
<Instance id="inst0" depth="1">
<UserData type="Instance">
<userValue title="occurrenceUUID" value="abc/>
</UserData>
<UserData type="Part" name="pqr">
<UserValue title="PartNumber" value="MTG_238_ZB_LACKIERUNG" />
</UserData>
</Instance>
</InstanceTree>
#XmlRootElement(name = "InstanceTree")
public class InstanceTree {
}
#XmlRootElement(name = "Instance")
public class Instance {
private List<Userdata> userdata;
#XmlElement
public List<Userdata> getUserdata() {
return userdata;
}
}
#XmlRootElement(name = "UserValue")
public class UserValue {
private List<UserValue> userValue;
#XmlElement
public List<UserValue> getUserValue() {
return userValue;
}
}
#XmlRootElement(name = "UserData")
public class Userdata {
}
In xml giving NullPointerException for upper case Instance, UserData, UserValue.
Giving error for xmlns="http://www.testsite.org/Schemas/abcSchema". working fine after removing url.
Use #XmlElement(name="Userdata") otherwise jaxb uses the attribute name.
#XmlRootElement(name = "Instance")
public class Instance {
private List<Userdata> userdata;
#XmlElement(name = "Userdata")
public List<Userdata> getUserdata() {
return userdata;
}
}
use also package-info.java
#javax.xml.bind.annotation.XmlSchema(
namespace = "http://www.testsite.org/Schemas/abcSchema",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "", namespaceURI = "http://www.testsite.org/Schemas/abcSchema")
}
)
package com.your.package;
I'm having trouble unmarshalling XML that represents an invoice in this form:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<Response xmlns="Lamp">
<Result>
<Title>The Title</Title>
<Lines>
<anyType xsi:type="InvoiceLine">
<Name>Name One</Name>
<Quantity>1.0000</Quantity>
</anyType>
<anyType xsi:type="InvoiceLine">
<Name>Name Two</Name>
<Quantity>2.0000</Quantity>
</anyType>
</Lines>
</Result>
</Response>
</soap:Body>
</soap:Envelope>
The Invoice class:
#XmlAccessorType(XmlAccessType.FIELD)
public class Invoice {
public String Title;
public List<InvoiceLine> Lines;
}
The InvoiceLine class:
#XmlAccessorType(XmlAccessType.FIELD)
public class InvoiceLine extends anyType {
public String Name;
public float quantity;
}
And a abstract class for anyType:
public abstract class anyType {}
This is the code I'm using to do the unmarshalling:
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("input.xml");
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(is);
xsr.nextTag();
while(!xsr.getLocalName().equals("Result")) {
xsr.nextTag();
}
JAXBContext jc = JAXBContext.newInstance(Invoice.class, InvoiceLine.class);
javax.xml.bind.Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Invoice> jb = unmarshaller.unmarshal(xsr, Invoice.class);
xsr.close();
System.out.println(jb.getValue());
}
The problem is the Lines list only contains 1 entry with Name=null and quantity=0.0.
-- Edit:
I've just tried adding XmlElement and XmlElementWrapper annotations to both classes, the only change is that the Lines list has no elements.
Modified Invoice:
#XmlAccessorType(XmlAccessType.FIELD)
public class Invoice {
#XmlElement(name = "Title")
public String Title;
#XmlElement(name = "anyType")
#XmlElementWrapper(name = "Lines")
public List<InvoiceLine> Lines;
}
Modified InvoiceLine:
#XmlAccessorType(XmlAccessType.FIELD)
public class InvoiceLine extends anyType {
#XmlElement(name = "Name")
public String Name;
#XmlElement(name = "Quantity")
public float Quantity;
}
-- Edit:
I've just tried making the attributes lower case, but still no luck.
Im using javax.xml.bind.annotation and I need to get the attribute "xmlns:language" (see below xml)
<type xmlns:language="ru" xmlns:type="string">Some text</type>
What annotation should I use?
#XmlRootElement(name = "type")
#XmlAccessorType(XmlAccessType.FIELD)
public class Type {
#XmlValue
protected String value;
#XmlAttribute
protected String language;
}
In the document from your question you are declaring that the prefix language will be associated with the namespace ru.
<type xmlns:language="ru" xmlns:type="string">Some text</type>
I do not believe the above is what you are trying to do. If you are trying to specify the language for your document I would recommend using the xml:lang attribute instead (as suggested by Ian Roberts).
<type xml:lang="ru">Some text</type>
Type
Then you would map to it as follows using the #XmlAttribute annotation:
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Type {
#XmlValue
protected String value;
#XmlAttribute(name = "lang", namespace = "http://www.w3.org/XML/1998/namespace")
protected String language;
}
Demo
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Type.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader(
"<type xml:lang='ru' xmlns:type='string'>Some text</type>");
Type type = (Type) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(type, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<type xml:lang="ru">Some text</type>
I'm having a problem using Moxy to marshal/unmarshal Salesforce outbound message XMLs. The exception that I'm getting is:
javax.xml.bind.JAXBException
- with linked exception:
[Exception [EclipseLink-44] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Missing class indicator field from database row [UnmarshalRecord()].
Descriptor: XMLDescriptor(service.salesforce.model.SObject --> [])]
The classes that I have are:
#XmlSeeAlso({Account.class, Opportunity.class})
#XmlDiscriminatorNode("#type")
public abstract class SObject {
public abstract String getId();
public abstract void setId(String id);
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlDiscriminatorValue("sf:Opportunity")
public class Opportunity extends SObject {
#XmlElement(name = "Id", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String id;
#XmlElement(name = "AccountId", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String accountId;
#XmlElement(name = "StageName", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String stageName;
#XmlElement(name = "Agreement_Date_Signed__c", namespace = "urn:sobject.enterprise.soap.sforce.com")
private Date agreementDateSigned;
#XmlElement(name = "Agreement_Date_Start__c", namespace = "urn:sobject.enterprise.soap.sforce.com")
private Date agreementDateStart;
#XmlElement(name = "Payment_Method__c", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String paymentMethod;
// getters and setters...
}
The XML that I'm trying to unmarshal is:
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:out="http://soap.sforce.com/2005/09/outbound" xmlns:urn="urn:sobject.enterprise.soap.sforce.com">
<soapenv:Header/>
<soapenv:Body>
<out:notifications>
<out:OrganizationId>123456789123456789</out:OrganizationId>
<out:ActionId>123456789123456789</out:ActionId>
<out:SessionId>sessId</out:SessionId>
<out:EnterpriseUrl>http://www.enterpriseexample.com</out:EnterpriseUrl>
<out:PartnerUrl>http://www.partnerexample.com</out:PartnerUrl>
<out:Notification>
<out:Id>987654321987654321</out:Id>
<out:sObject xsi:type="sf:Opportunity" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<urn:Id>121212121212121212</urn:Id>
<urn:AccountId>121212121212121212</urn:AccountId>
<urn:Agreement_Date_Signed__c>2012-01-01T08:34:56Z</urn:Agreement_Date_Signed__c>
<urn:Agreement_Date_Start__c>2012-02-01</urn:Agreement_Date_Start__c>
<urn:StageName>Booking</urn:StageName>
</out:sObject>
</out:Notification>
</out:notifications>
</soapenv:Body>
</soapenv:Envelope>
Any idea?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
Since you are using the xsi:type attribute as your discriminator node as your inheritance indicator, I would recommend using the standard JAXB mechanisms rather than MOXy's #XmlDescriminatorNode/#XmlDescriminatorValue extension.
SObject
package forum987537;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({Account.class, Opportunity.class})
public abstract class SObject {
public abstract String getId();
public abstract void setId(String id);
}
Opportunity
package forum987537;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="Opportunity")
public class Opportunity extends SObject {
private String id;
#Override
public String getId() {
return id;
}
#Override
public void setId(String id) {
this.id = id;
}
}
Notification
package forum987537;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Notification", namespace="http://soap.sforce.com/2005/09/outbound")
public class Notification {
private SObject sObject;
#XmlElement(namespace="http://soap.sforce.com/2005/09/outbound")
public SObject getsObject() {
return sObject;
}
public void setsObject(SObject sObject) {
this.sObject = sObject;
}
}
package-info
#XmlSchema(
namespace="urn:sobject.enterprise.soap.sforce.com",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix="out", namespaceURI = "http://soap.sforce.com/2005/09/outbound"),
#XmlNs(prefix="sf", namespaceURI = "urn:sobject.enterprise.soap.sforce.com"),
})
package forum987537;
import javax.xml.bind.annotation.*;
Demo
package forum987537;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Notification.class);
Notification notification = new Notification();
Opportunity opportunity = new Opportunity();
opportunity.setId("ABC123");
notification.setsObject(opportunity);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(notification, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<out:Notification xmlns:sf="urn:sobject.enterprise.soap.sforce.com" xmlns:out="http://soap.sforce.com/2005/09/outbound">
<out:sObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="sf:Opportunity">
<sf:id>ABC123</sf:id>
</out:sObject>
</out:Notification>
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
http://blog.bdoughan.com/2012/02/jaxb-and-inheritance-eclipselink-moxy.html