I am trying to create XML using the JaxB Marshalling
approach. I want to skip the parent tag for certain children or may add a new XML
parent tag for a certain element. Hence I am trying to use the @XmlPath
from import org.eclipse.persistence.oxm.annotations.XmlPath;
.
I am following the blog from the founder (Blog on @XmlPath) to make it work but for some reason, the @XmlPath
has no impact and the output XML does not match as shown in the blog. I followed all the process mentioned within the blog and also mentioned on various answers here.
All my classes are within the model.jaxb
package. Also, I have created a jaxb.properties
file within that package.
Following is my Customer
class:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@Getter
@Setter
@NoArgsConstructor
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath("contact-info/billing-address")
private Address billingAddress;
@XmlPath("contact-info/shipping-address")
private Address shippingAddress;
}
Following is my Address
class:
@Getter
@Setter
@NoArgsConstructor
public class Address {
private String street;
}
Following is Demo
class:
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Customer customer = new Customer();
Address billingAddress = new Address();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
Address shippingAddress = new Address();
shippingAddress.setStreet("2 Shipping Road");
customer.setShippingAddress(shippingAddress);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(customer, System.out);
}
}
The XML I getting with and without @XmlPath
are the same actually. So seems like the @XmlPath
has no impact during the marshalling.
<customer>
<billingAddress>
<street>1 Billing Street</street>
</billingAddress>
<shippingAddress>
<street>2 Shipping Road</street>
</shippingAddress>
</customer>
I have created jaxb.properties
file within my package model.jaxb
with the content:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
As per the blog the output XML should be something like this:
<customer>
<contact-info>
<billing-address>
<street>1 Billing Street</street>
</billing-address>
<shipping-address>
<street>2 Shipping Road</street>
</shipping-address>
</contact-info>
<customer>
I am not sure what am I doing wrong due to which I am unable to get the XML as I expect. I tried a lot of things but none worked so posting the same here.
Can someone please help me with how can I make the @XmlPath
kicking during the marshalling so that I can get the expected tag during the marshalling?
**** EDITED *****
I am using Java version 15.0.01 as per the Intellij IDE File -> Project Structure
. I saw one answer where it mentioned Moxy
does not work directly above Java 11
Answer Java 11.0 above.
So I did following things:
build
as mentioned in the above answer so my pom.xml
looks something like this: <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>model.jaxb</groupId>
<version>1.0-SNAPSHOT</version>
<artifactId>model-jaxb</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
</build>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sun.activation</groupId>
<artifactId>javax.activation</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.2</version>
</dependency>
</dependencies>
</project>
When I run the program then I get the error:
Exception in thread "main" java.lang.NoClassDefFoundError: jakarta/xml/bind/JAXBException
Caused by: java.lang.ClassNotFoundException: jakarta.xml.bind.JAXBException
I tried a lot of things mentioned on StackOverflow for this error and most of the answers mentioned adding the
dependency` I have added all the dependency mentioned in the answer. I am not sure what's going wrong.
Can someone please assist me with how can I fix this issue?
Finally was able to find the solution with a lot of assist from @andrewjames. Posting the solution for the same so somebody can find the solution quickly and do not end up spending whole day like me :)
You can also follow the steps mentioned by @andrewjames in his answer.
Make sure you have only ONE copy of jaxb.properties
file within the same package as of the domain class (the one which will be used during the marshalling in this case the Customer.class
). The content of the file should be:
jakarta.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
2.Remove all the dependency from your pom.xml
file and just add the following 2 dependencies:
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>3.0.0</version>
</dependency>
Customer.class
and Demo.ckass
) are using the imports from import jakarta.xml.bind.JAXBContext;
.Previously it was taking from import javax.xml.bind.JAXBContext;
should be replaced from import jakarta.xml.bind.JAXBContext;
.
I was making changes to Demo.class
and forgot to change in Customer.class
so was seeing some defclass
issue. So make sure all your class uses the imports from Jakarta
and not from Javax
.
<build>
to your pom.xml
: <build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
</build>
Demo.class
this should give you the expected XML. Also @XmlPath
should work as expected.Following are some of the mistakes I was making. You can check if you are not making them:
I was having too many dependencies and some were duplicated also. Such as dependencies from com.sun.activation
, javax.activation
, javax.validation
, org.glassfish.jaxb
, etc. These were mentioned in other answers so I was trying everything. Better remove all and make a fresh start and add only the once needed. For this case, only 2 mentioned in step-2
would be sufficient.
The answers mentioned in most of the post on Stackoverflow
is for the old version. Including the articles and blogs on various website. As there has been some changes after Java 11
better use the latest version and the steps mentioned in this answer or the other answer provided with the link above.
Make sure you clean and rebuild the maven project because sometimes the dependencies
are not directly added when you save the pom.xml
file. At least in my case when I was using the Intellij IDE
. I was doing clean and build every time I added the dependency so that I am making sure everything is fine from Maven
side.
That's all the points I feel sufficient for making this work. If you are still facing some issue then leave a comment on the other post or here. I will try to answer if I can. Have a nice day.