javaxmljaxbeclipselinkmoxy

@XmlPath has no impact during the JAXB Marshalling


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:

  1. I added the dependency and 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?


Solution

  • 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.

    1. 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>
    
    1. Make sure all your classes (in this case 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.

    1. Add the following <build> to your pom.xml:
          <build>
            <resources>
              <resource>
                <directory>src/main/java</directory>
                <excludes>
                  <exclude>**/*.java</exclude>
                </excludes>
              </resource>
            </resources>
          </build>
    
    1. After making these changes run the 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:

    1. 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.

    2. 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.

    3. 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.