JAXB : How to add attributes to an inner element - java

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;
}
}
}
}

Related

Two different schemas and JAXB Marshaller

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>

There's no ObjectFactory with an #XmlElementDecl when there is static class

I am getting below exception, i need some help to resolve the issue.
If remove the namespace in the object factory and with out package-info.java class it is working fine.
Exception that is throwing now
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
There's no ObjectFactory with an #XmlElementDecl for the element {}shipping.
this problem is related to the following location:
at protected javax.xml.bind.JAXBElement com.jverstry.annotations.generics.Market$Detail.shipping
at com.jverstry.annotations.generics.Market$Detail
at protected com.jverstry.annotations.generics.Market$Detail com.jverstry.annotations.generics.Market.detail
at com.jverstry.annotations.generics.Market
ObjectFactory class which is creating the jaxbelement
package com.jverstry.annotations.generics;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
import org.example.customer.Customer;
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Market.Detail.Shipping createShipping() {
return new Market.Detail.Shipping();
}
private final static QName _Shipping_QNAME = new QName("http://www.example.org/customer", "shipping");
#XmlElementDecl(namespace = "http://www.example.org/customer", name = "shipping")
public JAXBElement<Market.Detail.Shipping> createShipping(Market.Detail.Shipping value) {
return new JAXBElement<Market.Detail.Shipping>(_Shipping_QNAME, Market.Detail.Shipping.class, value);
}
}
Class package-info.java, where the name spaces are mentioned for the response xml
#XmlSchema(namespace = "http://www.example.org/customer", elementFormDefault = XmlNsForm.QUALIFIED)
package com.jverstry.annotations.generics;
import javax.xml.bind.annotation.*;
Demo class where marshalling object
package com.jverstry.annotations.generics;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Market.class);
Market market = new Market();
Market.Detail md = new Market.Detail();
Market.Detail.Shipping mds = new Market.Detail.Shipping();
mds.setAvailable(false);
JAXBElement<Market.Detail.Shipping> shipping = new ObjectFactory().createShipping(mds);
shipping.setNil(true);
md.setShipping(shipping);
market.setDetail(md);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(market, System.out);
}
}
Market class, this is the main root class where jaxbcontext is created
package com.jverstry.annotations.generics;
import java.math.BigDecimal;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "detail" })
#XmlRootElement(name = "Market")
public class Market
{
#XmlElement(required = false)
protected Market.Detail detail;
public Market.Detail getDetail() {
return detail;
}
public void setDetail(Market.Detail detail) {
this.detail = detail;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "shipping" })
public static class Detail
{
#XmlElementRef(name = "shipping")
protected JAXBElement<Market.Detail.Shipping> shipping;
public JAXBElement<Market.Detail.Shipping> getShipping() {
return shipping;
}
public void setShipping(JAXBElement<Market.Detail.Shipping> value) {
this.shipping = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "value" })
public static class Shipping
{
#XmlValue
protected BigDecimal value;
#XmlAttribute(name = "available")
protected Boolean available;
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
public Boolean getAvailable() {
return available;
}
public void setAvailable(Boolean value) {
this.available = value;
}
}
}
}
You need to create the JAXBContext by passing in the ObjectFactory class or the package name of the generated model to ensure the ObjectFactory class is processed.
If you specify the namespace property on the #XmlElementRef annotation things should work.

JAXB : annotation. Exception raised

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

JAVA-JAXB/ define class as field

I'm using jaxb, and as I understood, we define for each element and attribute field, and jaxb reconize this attribute, and put it value into this elemnt. My problem that one of my attribute is class:
<div class="hello"> Hi </div>
so I want to define in the class div, the next:
String class;
public String getClass() {
return class;
}
#XmlAttribute
public void setClass(String class) {
this.class = class;
}
But I can't because - Syntax error on token "class", invalid VariableDeclarator
What can I do?
You can use the name property of XmlAttribute to specify a different name for the mapping variable.
String className;
public String getClassName() {
return clazz;
}
#XmlAttribute(name="class")
public void setClassName(String className) {
this.className = className;
}
It is working perfectly fine
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
public class Test {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Div.class);
Div div = new Div();
div.setClassName("new-item");
StringWriter sw = new StringWriter();
context.createMarshaller().marshal(div, sw);
System.out.println(sw.toString());
String s = "<div class=\"hello\"> Hi </div>";
Div object = (Div) context.createUnmarshaller().unmarshal(new StringReader(s));
System.out.println(object.getClassName());
}
#XmlType(name = "div")
#XmlRootElement(name = "div")
public static class Div {
private String className;
#XmlAttribute(name = "class")
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
}

Adding an Attribute to a JAXB Element

I'm struggling with some JAXB parsing and need some guidance.
Essentially, I'm trying to add attributes to my class variables that I have already declared as Elements using #XmlElement. So far, any attempt to use #XmlAttribute sets the attribute at the class level.
What I"m currently getting is this:
<DataClass newAttribute="test">
<myElement>I wish this element had an attribute</myElement>
<anotherElement>I wish this element had an attribute too</anotherElement>
</DataClass>
I'd like to to do this:
<DataClass>
<myElement thisAtt="this is what I'm talking about">This is better</myElement>
<anotherElement thisAtt="a different attribute here">So is this</anotherElement>
</DataClass>
I've seen other posts add an attribute to a single element using the #XmlValue, but that doesn't work when you have Elements, and won't work on multiple elements.
Does anyone have a thought on how this could be accomplished?
Thanks!
Jason
This will create that XML:
public class JaxbAttributes {
public static void main(String[] args) throws Exception {
Marshaller marshaller =
JAXBContext.newInstance(DataClass.class).createMarshaller();
StringWriter stringWriter = new StringWriter();
DataClass dataClass = new DataClass(
new Foo("this is what I'm talking about", "This is better"),
new Foo("a different attribute here", "So is this"));
marshaller.marshal(dataClass, stringWriter);
System.out.println(stringWriter);
}
#XmlRootElement(name = "DataClass")
#XmlType(propOrder = {"myElement", "anotherElement"})
static class DataClass {
private Foo myElement;
private Foo anotherElement;
DataClass() {}
public DataClass(Foo myElement, Foo anotherElement) {
this.myElement = myElement;
this.anotherElement = anotherElement;
}
public Foo getMyElement() { return myElement; }
public void setMyElement(Foo myElement) { this.myElement = myElement; }
public Foo getAnotherElement() { return anotherElement; }
public void setAnotherElement(Foo anotherElement) { this.anotherElement = anotherElement; }
}
static class Foo {
private String thisAtt;
private String value;
Foo() {}
public Foo(String thisAtt, String value) {
this.thisAtt = thisAtt;
this.value = value;
}
#XmlAttribute
public String getThisAtt() { return thisAtt; }
public void setThisAtt(String thisAtt) { this.thisAtt = thisAtt; }
#XmlValue
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead, and a member of the JAXB 2.X (JSR-222) expert group.
Alternatively you could use the #XmlPath extension in MOXy to handle this use case:
DataClass
The #XmlPath annotation can be used with the standard JAXB annotations:
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="DataClass")
#XmlType(propOrder={"myElement", "anotherElement"})
public class DataClass {
private String myElement;
private String myElementThisAtt;
private String anotherElement;
private String anotherElementThisAtt;
public String getMyElement() {
return myElement;
}
public void setMyElement(String myElement) {
this.myElement = myElement;
}
#XmlPath("myElement/#thisAtt")
public String getMyElementThisAtt() {
return myElementThisAtt;
}
public void setMyElementThisAtt(String myElementThisAtt) {
this.myElementThisAtt = myElementThisAtt;
}
public String getAnotherElement() {
return anotherElement;
}
public void setAnotherElement(String anotherElement) {
this.anotherElement = anotherElement;
}
#XmlPath("anotherElement/#thisAtt")
public String getAnotherElementThisAtt() {
return anotherElementThisAtt;
}
public void setAnotherElementThisAtt(String anotherElementThisAtt) {
this.anotherElementThisAtt = anotherElementThisAtt;
}
}
Demo
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(DataClass.class);
DataClass dataClass = new DataClass();
dataClass.setMyElement("This is better");
dataClass.setMyElementThisAtt("this is what I'm talking about");
dataClass.setAnotherElement("So is this");
dataClass.setAnotherElementThisAtt("a different attribute here");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(dataClass, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<DataClass>
<myElement thisAtt="this is what I'm talking about">This is better</myElement>
<anotherElement thisAtt="a different attribute here">So is this</anotherElement>
</DataClass>
More Information
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
http://bdoughan.blogspot.com/2011/05/specifying-eclipselink-moxy-as-your.html

Categories