I have a REST service which serialize into the response some objects.
My entities ares annotated with XML but JAXB raised an illegalAnnotationExceptions...
Here the entities :
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "icns")
public class IcnList {
#XmlElement(required = true)
private List<IcnElement> icns;
public List<IcnElement> getIcns() {
return icns;
}
public void setIcns(List<IcnElement> icns) {
this.icns = icns;
}
}
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "icn")
public class IcnElement {
private String status;
private String revision;
private String icnName;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getRevision() {
return revision;
}
public void setRevision(String revision) {
this.revision = revision;
}
public String getIcnName() {
return icnName;
}
public void setIcnName(String icnName) {
this.icnName = icnName;
}
}
Here the exception :
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "icns"
this problem is related to the following location:
at public java.util.List com.xx.model.IcnList.getIcns()
at com.xx.model.IcnList
this problem is related to the following location:
at private java.util.List com.xx.model.IcnList.icns
at com.xx.model.IcnList
Can someone tell me what is the problem ? and why ?
I made some research but I'm totally lost...
Thank you.
By default JAXB will treat public properties and annotated fields as mapped. The conflict is occurring in your mapping because JAXB thinks you have the following mappings:
A field called icns that is mapped to the element icns.
A property called icns that is mapped to the element icns.
This is causing your name conflict. You can eliminate the conflict by annotating the property (get or set method):
#XmlRootElement(name = "icns")
public class IcnList {
private List<IcnElement> icns;
#XmlElement(required = true)
public List<IcnElement> getIcns() {
return icns;
}
public void setIcns(List<IcnElement> icns) {
this.icns = icns;
}
}
Or if you wish to annotate the field you can use #XmlAccessorType(XmlAccessType.FIELD) at the class level.
#XmlRootElement(name = "icns")
#XmlAccessorType(XmlAccessType.FIELD)
public class IcnList {
#XmlElement(required = true)
private List<IcnElement> icns;
public List<IcnElement> getIcns() {
return icns;
}
public void setIcns(List<IcnElement> icns) {
this.icns = icns;
}
}
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Change the name of the root element
#XmlRootElement(name = "icns")
or, this element:
#XmlElement(required = true)
private List<IcnElement> icns;
Use #XmlType(name = "icn" ....) instead
Related
I'm trying to unmarshall this kind of XML:
<result>
<avance>0.0000</avance>
<operation_status>0</operation_status>
<service>Bank Account</service>
<service>
<min_amount>1.00</min_amount>
<max_amount>1499.00</max_amount>
<currency>USD</currency>
</service>
</result>
I have created this kind of class structure:
public class Result {
private BigDecimal avance;
private Integer operationStatus;
private String serviceDesc;
private Service service;
#XmlElement(name = "service")
public String getServiceDesc() {
return serviceDesc;
}
public void setServiceDesc(String serviceDesc) {
this.serviceDesc = serviceDesc;
}
#XmlElement(name = "service")
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
#XmlElement(name = "avance")
public BigDecimal getAvance() {
return avance;
}
public void setAvance(BigDecimal avance) {
this.avance = avance;
}
#XmlElement(name = "operation_status")
public Integer getOperationStatus() {
return operationStatus;
}
public void setOperationStatus(Integer operationStatus) {
this.operationStatus = operationStatus;
}
}
and Service looks like this:
#XmlRootElement
public class Service {
private BigDecimal minAmount;
private BigDecimal maxAmount;
private String currency;
#XmlElement(name = "min_amount")
public BigDecimal getMinAmount() {
return minAmount;
}
public void setMinAmount(BigDecimal minAmount) {
this.minAmount = minAmount;
}
#XmlElement(name = "max_amount")
public BigDecimal getMaxAmount() {
return maxAmount;
}
public void setMaxAmount(BigDecimal maxAmount) {
this.maxAmount = maxAmount;
}
#XmlElement(name = "currency")
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
}
When I receive response from some external service I'm able to unmarshal that string and result has correct Service class set but serviceDesc is always null. Is there any way how I can unmarshall this structure correctly?
In another question (for which this question is marked as duplicate) case is when you have same tag name but different number of attributes in my case content of the tag is different, also in that question classes to which content should be unmarshaled are child and parent in my case one is String another one some custom object. Think that's why I wasn't able to implement XmlAdapter properly.
Your XML structure is not valid.
You shouldn't have the same tag twice for different structure in the same namespace.
Solutions :
Rename your tag (serviceDesc for example)
Use 2 different namespaces ( ns1:service and ns2:service for example)
I have a class annotated as follows:
#XmlRootElement(name="response")
#XmlType(propOrder={"paymentid",
"result",
"responsecode",
"authorizationcode",
"merchantorderid",
"rrn",
"cardcountry",
"cardtype"})
public class MOTOResponseIn {
...
}
The root element of the mapped XML could be also be error beside
response.
How can I annotate the class so that both root elements are allowed?
In this case #XmlRootElement can not be used.
You have to use ObjectFactory.
The #XmlElementDecl annotation is used to represent root elements that correspond to named complex types. It is placed on a factory method in a class annotated with #XmlRegistry (when generated from an XML schema this class is always called ObjectFactory). The factory method returns the domain object wrapped in an instance of JAXBElement
Hope this url will help.
https://dzone.com/articles/jaxb-and-root-elements
With a single class and #XmlRootElement it is not possible.
However, in case you don't want to mess with ObjectFactory, for a quick workaround you can use abstract and concrete classes. (Assuming the only difference is the root element)
Example:
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class MOTOabstract{
#XmlAttribute
private String paymentid;
#XmlAttribute
private String result
#XmlAttribute
private String responsecode;
...
}
#XmlRootElement(name="response")
#XmlAccessorType(XmlAccessType.FIELD)
public class MOTOresponse extends MOTOabstract{}
#XmlRootElement(name="error")
#XmlAccessorType(XmlAccessType.FIELD)
public class MOTOerror extends MOTOabstract{}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"id",
"name",
"serviceAttrs"
})
#XmlSeeAlso({ AddGroup.class, AddGroupRequest.class })
public class AddGroupAbstract {
#XmlElement(required = true)
protected String id;
#XmlElement(required = true)
protected String name;
#XmlElement(required = true)
protected ServiceAttrs serviceAttrs;
...
}
#XmlRootElement(name = "addGroup")
public class AddGroup extends AddGroupAbstract {}
#XmlRootElement(name = "addGroupRequest")
public class AddGroupRequest extends AddGroupAbstract {}
#Endpoint
public class GroupEndpoint {
private final GroupService groupService;
private final ServiceService serviceService;
private final RestTemplate restTemplate;
public GroupEndpoint(GroupService groupService, ServiceService serviceService, RestTemplate restTemplate) {
this.groupService = groupService;
this.serviceService = serviceService;
this.restTemplate = restTemplate;
}
#PayloadRoots({
#PayloadRoot(namespace = SoapConstants.NAMESPACE_ACCOUNT_URI, localPart = "addGroup"),
#PayloadRoot(namespace = SoapConstants.NAMESPACE_ACCOUNT_URI, localPart = "addGroupRequest")
})
#ResponsePayload
public AddGroupResponse addGroup(#RequestPayload AddGroupAbstract request) {
AddGroupResponse response = new AddGroupResponse();
...
}
}
Lets suppose that we have XML consnensual with Schema and Java class with some common fields:
<objectFromSchema1>
<element1/>
<commonElement1/>
<commonElement2/>
<element2/>
</objectFromSchema1>
public class X {
private String element1;
private String commonElement1;
private String commonElement2;
private String element2;
}
Is a nice way to unmarschall such kind of XML to Java object ? It means: convert all consensual fields and set null on rest.
The answer is "yes". This is the way JAXB works. Take a look on basic JAXB tutorial, e.g. https://jaxb.java.net/tutorial/
http://docs.oracle.com/javase/tutorial/jaxb/intro/
http://www.vogella.com/tutorials/JAXB/article.html
"YES"
If you have an xsd you also could generate automatically these classes by <artifactId>maven-jaxb2-plugin</artifactId>maven plugin.
An example of your Class
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "objectFromSchema1", propOrder = {
})
#XmlRootElement(name = "objectFromSchema1")
public class ObjectFromSchema1
implements Serializable
{
private final static long serialVersionUID = 12343L;
protected String element1;
protected String element2;
protected String commonElement1;
protected String commonElement2;
public String getElement1() {
return element1;
}
public void setElement1(String element1) {
this.element1 = element1;
}
public String getElement2() {
return element2;
}
public void setElement2(String element2) {
this.element2 = element2;
}
public String getCommonElement1() {
return commonElement1;
}
public void setCommonElement1(String commonElement1) {
this.commonElement1 = commonElement1;
}
public String getCommonElement2() {
return commonElement2;
}
public void setCommonElement2(String commonElement2) {
this.commonElement2 = commonElement2;
}
}
Main method to use it
public static void main(String[] args) throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(ObjectFromSchema1.class);
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
final ObjectFromSchema1 objectFromSchema1 = new ObjectFromSchema1();
objectFromSchema1.setCommonElement1("commonElement1");
objectFromSchema1.setCommonElement2("commonElement2");
objectFromSchema1.setElement1("element1");
objectFromSchema1.setElement2("element2");
m.marshal(objectFromSchema1, System.out);
}
output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectFromSchema1>
<element1>element1</element1>
<element2>element2</element2>
<commonElement1>commonElement1</commonElement1>
<commonElement2>commonElement2</commonElement2>
</objectFromSchema1>
I have the below XML that I want to read. Using JAXB on java 1.6, how do I annotate for the attribute regex ? Can I have the field to be of type boolean ?
<?xml version="1.0" encoding="utf-8"?>
<authStore>
<authList>
<auth>
<resource>res1</resource>
<privilege regex = "true">PRIV_FILE_.+?_READ</privilege>
</auth>
<auth>
<resource>res2</resource>
<privilege>PRIV_FILE_READ</privilege>
</auth>
</authStore>
UPDATE : Is it possible to make the attribute optional ? If yes, when I unmarshal, will I get regex field to be false when a privilege element does not have the optional attribute regex ?
UDPATE2 : I don't want to define separate classes for resource and privilege. Also, I don't want to use MOXy. Pls. suggest solution for sun/oracle JDK 1.6 JAXB only.
UPDATE3 : My current object model is something like this
// AuthStore.java
#XmlRootElement
public class AuthStore {
#XmlElementWrapper(name = "authList")
#XmlElement(name = "auth")
private ArrayList<Auth> authList;
public void setAuthList(ArrayList<Auth> authList) {
this.authList = authList;
}
public ArrayList<Auth> getAuthsList() {
return authList;
}
}
// Auth.java
#XmlRootElement(name = "auth")
#XmlType(propOrder = { "resource", "privilege" })
public class Auth
{
private String resource;
private String privilege;
#XmlElement(name = "resource")
public String getResource()
{
return resource;
}
public void setResource(String resource)
{
this.resource = resource;
}
#XmlElement(name = "privilege")
public String getPrivilege()
{
return privilege;
}
public void setPrivilege(String author)
{
this.privilege = author;
}
}
Because privilege contains an attribute (It's actually complex type), you must create a class to hold both the value and the attribute:
import java.io.InputStream;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement(name = "authStore")
#XmlAccessorType(XmlAccesssType.FIELD)
public class AuthStore {
public static void main(String []args) throws Exception {
InputStream inputStream = AuthStore.class.getResourceAsStream("test.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(AuthStore.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
AuthStore authStore = (AuthStore)unmarshaller.unmarshal(inputStream);
System.out.println(authStore.getAuthList().get(0).getResource());
System.out.println(authStore.getAuthList().get(0).getPrivilege().getRegex());
System.out.println(authStore.getAuthList().get(0).getPrivilege().getValue());
}
#XmlElementWrapper(name = "authList")
#XmlElement(name = "auth")
private List<Auth> authList;
public List<Auth> getAuthList() {
return authList;
}
#XmlAccessorType(XmlAccesssType.FIELD)
public static class Auth {
#XmlElement(name = "resource")
private String resource;
#XmlElement(name = "privilege")
private Privilege privilege;
public String getResource() {
return resource;
}
public Privilege getPrivilege() {
return privilege;
}
#XmlAccessorType(XmlAccesssType.FIELD)
public static class Privilege {
#XmlAttribute(name = "regex")
private Boolean regex;
#XmlValue
private String value;
public Boolean getRegex() {
return regex;
}
public String getValue() {
return value;
}
}
}
}
In my data model, I have something to this effect:
#Entity
public class Target {
#Id
#GeneratedValue
private Long id;
/* ...etc... */
}
#Entity
public class Dependency {
#Id
#GeneratedValue
private Long id;
#ManyToOne(optional=false)
#Column(name="target_id")
private Target target;
/* ...etc... */
}
I'm already serializing Target just fine, but I need to serialize Dependency. Essentially, what I need is something like this:
<dependency>
<id>100</id>
<targetId>200</targetId>
</dependency>
Is there a way to do this in JAXB annotations without modifying my model?
You could use an XmlAdapter for this use case:
package forum7278406;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class TargetAdapter extends XmlAdapter<Long, Target> {
#Override
public Long marshal(Target target) throws Exception {
return target.getId();
}
#Override
public Target unmarshal(Long id) throws Exception {
Target target = new Target();
target.setId(id);
return target;
}
}
The XmlAdapter is registered on the Dependency class using the #XmlJavaTypeAdapter annotation:
package forum7278406;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Dependency {
#Id
#GeneratedValue
private Long id;
#ManyToOne(optional=false)
#Column(name="target_id")
#XmlJavaTypeAdapter(TargetAdapter.class)
private Target target;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Target getTarget() {
return target;
}
public void setTarget(Target target) {
this.target = target;
}
}
Going Further
Instead of just creating a new instance of Target we could use an EntityManager to query the corresponding instance from the database. Our XmlAdapter would be changed to look something like:
package forum7278406;
import javax.persistence.EntityManager;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class TargetAdapter extends XmlAdapter<Long, Target> {
EntityManager entityManager;
public TargetAdapter() {
}
public TargetAdapter(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public Long marshal(Target target) throws Exception {
return target.getId();
}
#Override
public Target unmarshal(Long id) throws Exception {
Target target = null;
if(null != entityManager) {
target = entityManager.find(Target.class, id);
}
if(null == target) {
target = new Target();
target.setId(id);
}
return target;
}
}
Now to set the instance of EntityManager on our XmlAdapter, we can do the following:
Unmarshaller umarshaller = jaxbContext.createUnmarshaller();
TargetAdapter targetAdatper = new TargetAdapter(entityManager);
unmarshaller.setAdapter(targetAdapter);
It works for EclipseLink MOXy with XmlID and XmlIDRef (but fails for sun JAXB, where XmlID must be string)
#Entity
#XmlRootElement
public class Target {
#Id
#GeneratedValue
#XmlID
#XmlElement
private Long id;
}
#Entity
#XmlRootElement
public class Dependency {
#Id
#GeneratedValue
#XmlElement
private Long id;
#ManyToOne(optional = false)
#Column(name = "target_id")
#XmlIDREF
#XmlElement(name = "targetId")
private Target target;
}