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>
Related
I'm using JAXB for creating xml. I want to set attribute 'lang' on elements PrimaryValue and AlternativeSpelling.
<AgencyOrUnit>
<PrimaryValue lang="el">ΓΑΔΑ</PrimaryValue>
<AlternativeSpelling lang="en">Athens General Police Directorate</AlternativeSpelling>
</AgencyOrUnit>
Here's my code:
#XmlRootElement(name = "OwnerReference")
#XmlType(propOrder = { "primaryValue", "alternativeSpelling"})
public class AgencyOrUnit {
private String PrimaryValue;
private String AlternativeSpelling;
public String getPrimaryValue() {
return PrimaryValue;
}
public void setPrimaryValue(String PrimaryValue){
this.PrimaryValue = PrimaryValue;
}
public String getAlternativeSpelling() {
return AlternativeSpelling;
}
public void setAlternativeSpelling(String AlternativeSpelling){
this.AlternativeSpelling = AlternativeSpelling;
}
}
Here's process of marshalling:
AgencyOrUnit agencyOrUnit = new AgencyOrUnit();
agencyOrUnit.setPrimaryValue("ΓΑΔΑ");
agencyOrUnit.setAlternativeSpelling("General Police");
The problem is that I don't know how to set property with value on elements primaryValue and alternativeSpelling?
You can use annotations #XmlValue & #XmlAttribute but you need to create a new class to hold both lang and the original value string. Something like this:
#Setter
#AllArgsConstructor
public class LocaleString {
private String lang;
private String value;
#XmlAttribute
public String getLang() {
return lang;
}
#XmlValue
public String getValue() {
return value;
}
}
Then modify your AgencyOrUnit accordingly:
#XmlRootElement(name = "OwnerReference")
#XmlType(propOrder = { "primaryValue", "alternativeSpelling"})
#Getter #Setter
public class AgencyOrUnit {
private LocaleString PrimaryValue;
private LocaleString AlternativeSpelling;
}
Test it:
#Test
void test() throws JAXBException {
AgencyOrUnit agencyOrUnit = new AgencyOrUnit();
agencyOrUnit.setPrimaryValue(new LocaleString("el", "ΓΑΔΑ"));
agencyOrUnit.setAlternativeSpelling(new LocaleString("en", "General Police"));
JAXBContext ctx = JAXBContext.newInstance(AgencyOrUnit.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(agencyOrUnit, System.out);
}
and you should see this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OwnerReference>
<primaryValue lang="el">ΓΑΔΑ</primaryValue>
<alternativeSpelling lang="en">General Police</alternativeSpelling>
</OwnerReference>
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.
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;
}
}
}
}
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
When marshaling an object via JAXB with a StringBuffer attribute, that attribute becomes blank. I wrote a small program to demonstrate the problem:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class JaxbTest {
private String valueOne;
private StringBuffer valueTwo;
public static void main(String[] args) throws Exception {
JaxbTest object = new JaxbTest();
object.setValueOne("12345");
object.setValueTwo(new StringBuffer("54321"));
JAXBContext context = JAXBContext.newInstance(JaxbTest.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(object, System.out);
}
#XmlElement
public String getValueOne() {
return valueOne;
}
public void setValueOne(String valueOne) {
this.valueOne = valueOne;
}
#XmlElement
public StringBuffer getValueTwo() {
return valueTwo;
}
public void setValueTwo(StringBuffer valueTwo) {
this.valueTwo = valueTwo;
}
}
The output is as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><jaxbTest><valueOne>12345</valueOne><valueTwo/></jaxbTest>
Does anyone know why "valueTwo" is not being marshaled correctly? BTW, i am using java 1.6.0_22.
Thanks in advance!!!
I would recommend using JAXB's XmlAdapter for this use case:
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
It is likely that JaxB does not know how to serialize a StringBuffer. What I would do to solve this kind of issues, is to have a pair of getters/setters:
the one you currently have
one which returns a String and annotated with #XmlElement
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class JaxbTest {
private String valueOne;
private StringBuffer valueTwo;
public static void main(String[] args) throws Exception {
JaxbTest object = new JaxbTest();
object.setValueOne("12345");
object.setValueTwo(new StringBuffer("54321"));
JAXBContext context = JAXBContext.newInstance(JaxbTest.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(object, System.out);
}
#XmlElement
public String getValueOne() {
return valueOne;
}
public void setValueOne(String valueOne) {
this.valueOne = valueOne;
}
public StringBuffer getValueTwo() {
return valueTwo;
}
public void setValueTwo(StringBuffer valueTwo) {
this.valueTwo = valueTwo;
}
#XmlElement
public String getValueTwoString() {
return valueTwo!=null?valueTwo.toString():null;
}
public void setValueTwoString(String valueTwo) {
this.valueTwo = new StringBuffer(valueTwo);
}
}
I am not completely sure, but I think that if you use #XmlElement(name="valueTwo") on the getValueTwoString() method, you should get exactly what you want.
When I have marshaling issues with simple types, I tend to create an extra getter (and possibly setter) to simplify it. Then I add an #XmlIgnore to the main field and set the name of the new field to that of the old one. Example below:
#XmlRootElement
public class JaxbTest {
private String valueOne;
private StringBuffer valueTwo;
public static void main(String[] args) throws Exception {
JaxbTest object = new JaxbTest();
object.setValueOne("12345");
object.setValueTwo(new StringBuffer("54321"));
JAXBContext context = JAXBContext.newInstance(JaxbTest.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(object, System.out);
}
#XmlElement
public String getValueOne() {
return valueOne;
}
public void setValueOne(String valueOne) {
this.valueOne = valueOne;
}
#XmlIgnore
public StringBuffer getValueTwo() {
return valueTwo;
}
public void setValueTwo(StringBuffer valueTwo) {
this.valueTwo = valueTwo;
}
#XmlElement(name="valueTwo")
public String getValueTwoString() {
return valueTwo.toString();
}
public void setValueTwoString(String valueTwo) {
this.valueTwo = new StringBuffer(valueTwo);
}
}
Thanks for all the prompt and wonderful answers!!
I got this problem when using the servicemix-exec component in ServiceMix 4.2, which is caused by this ExecResponse class. It is using a StringBuffer for the "outputData" and "errorData" attribute.