1

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?

BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98
  • Does your `pom.xml` file have a build step which ensures the `jaxb.properties` file is bundled into the JAR (or WAR) built by Maven? – andrewJames May 12 '21 at 13:20
  • I added a note about the required Maven build step to my answer in [this other question](https://stackoverflow.com/a/60285555/12567365), in case that helps. Apart from that, I am not able to recreate your problem. Your code generates the expected XML when I use it with this extra build step (but not without!). – andrewJames May 12 '21 at 13:26
  • Thanks a lot for checking out and providing the answer. I was somewhat able to figure out the issue. I added my `jaxb.properties` file within the folder `resources->model->jaxb` same structure as my `domain classes which will be used by jaxb` after that I added the dependecy mentioned in your answer and two more dependencies such as `org.glassfish.jaxb` and `jakarta.xml.bind`. Now its throwing me the error `Exception in thread "main" java.lang.NoClassDefFoundError: jakarta/xml/bind/JAXBContext` – BATMAN_2008 May 12 '21 at 13:44
  • OK - but if you revert to the dependencies in my other answer, and if you add the **step regarding the Maven build,** then does it work for you (because your code works for me). Then you can start adjusting dependencies if you need to - but you will be doing that from a config which works. – andrewJames May 12 '21 at 14:41
  • Thanks for your response and taking time. I actually followed the steps mentioned in your answer and lots of other resources and made the modification accordingly but still I am seeing that `Exception in thread "main" java.lang.NoClassDefFo undError: jakarta/xml/bind/JAXBContext` issue. I have modified the question based on this issue. Can you please have look at the `edited` section in this question and provide your guidence? – BATMAN_2008 May 12 '21 at 15:44
  • Regarding your notes _"I added the jaxb.properties file within my resource folder"_: Your properties file should not be placed in a `resources` folder. It needs to be in the same location as your Java source files - namely the `model.jaxb` package. Creating a copy of that directory structure in `resources` is a different thing entirely. Your Maven `build` command will ensure that the properties file is copied to the same target location as your compiled `class` files. You can open the JAR file to check it ends up in the right place. – andrewJames May 12 '21 at 15:55
  • @andrewjames Actually I placed both places in the `Java Classes` folder and in the `resources` folder just to see if that would make any difference. Now after your comment I removed the file from the `Resources` folder. So now it's just present within the same package as my `java classes`. This is providing me the XML but the XML does not take the `@XmlPath` elements into account. With or without the `@XmlPath` it's producing the same XML so I am not sure what's going wrong here. Because you used the same code and it's working for you but for some reason its not working for me. What can I try? – BATMAN_2008 May 12 '21 at 16:21
  • The only thing I can see that is different between your code and mine is the use of Lombok (I am not using it). That should not make any difference... – andrewJames May 12 '21 at 16:26
  • Actually, the error still exists after removing the `jaxb.properties` file from the `resources` folder. Most of the answer mention that the error is due to the missing `dependency` I have added all the required dependencies but still get the error: `Exception in thread "main" java.lang.NoClassDefFoundError: jakarta/xml/bind/JAXBException` – BATMAN_2008 May 12 '21 at 16:35
  • Why do you have `jakarta.xml.bind` in your dependencies? – andrewJames May 12 '21 at 16:38
  • That's what most of the answers mentioned for the issue so I was trying that as well: `jakarta.xml.bind` and `org.glassfish.jaxb`. For Java 11 and above they are saying we need to use this dependency. If I remove that then also I am getting errors. If I add then also I am getting errors. I am very much confused about what needs to be done. Should I remove both or just the one `jakarta.xml.bind`? Here is a link to what I was referring: https://stackoverflow.com/a/43574427/7584240 – BATMAN_2008 May 12 '21 at 16:44
  • Remove all your dependencies. Then add back just these two: `javax.xml.bindjaxb-api2.3.1` and `org.eclipse.persistenceeclipselink2.7.6`. Tell me what happens. – andrewJames May 12 '21 at 16:47
  • Sure, so I removed all of my dependencies and added just the 3 dependencies which you provided (and Lombok) and ran the `Demo.class` which has the `JaxBContent`. I am getting the following error: `Exception in thread "main" java.lang.NoClassDefFoundError: jakarta/xml/bind/JAXBException` after few lines of error I have this line as well `Caused by: java.lang.ClassNotFoundException: jakarta.xml.bind.JAXBException` – BATMAN_2008 May 12 '21 at 17:12
  • This should work for an **older** version of MOXy (v2.7). Then to upgrade to the **latest & greatest** version of MOXy, see my latest updates to [this answer](https://stackoverflow.com/questions/39388961/not-able-to-configure-moxy-using-jaxb/60285555#60285555). See the new section "UPDATES for MOXy version 3". – andrewJames May 12 '21 at 17:18
  • I only provided 2 dependencies not 3, in my comment. Sorry - but I don't know what changes you are making - they do not match my comments. Anyway, see my notes about "latest and greatest". – andrewJames May 12 '21 at 17:20
  • Hi, I am really sorry I added just those 2 dependencies including Lombok 3 I meant I am really sorry for the mistake. Actually, I am not making any changes apart from what you have asked me to do. Now I am trying to implement based on your updated answer from your post. – BATMAN_2008 May 12 '21 at 17:22
  • OK - good luck! – andrewJames May 12 '21 at 17:29
  • I tried the updated code from your post. 1. I updated the `jaxb.properties` file content to `jakarta` (replaced old line with a new one). 2. Added the new dependencies to `pom.xml` with versions 3.0.0 (removed old and just kept new dependencies). 3. Changed the imports in `Demo.class` to `jakarta` from `jaxb`. when I run I am getting following error: `Exception in thread "main" javax.xml.bind.JAXBException: jaxb.properties in package (link to package) does not contain the javax.xml.bind.JAXBContextFactory property.` – BATMAN_2008 May 12 '21 at 17:31
  • I recommend starting again from scratch with a completely new Java project. I think there have been so many changes you have tried, that something is seriously out-of-step now in your code/config. These errors are basically "impossible" given the dependencies you should be using. (Also, do make sure you are performing a complete clean & build after every change you make.) And try without Lombok, to start with. Add it back in once you have something that actually works. – andrewJames May 12 '21 at 17:35
  • Sure, I will follow your instructions and create from scratch. Post my observations here after trying. Thanks a lot again for being so patient and guiding. – BATMAN_2008 May 12 '21 at 17:40
  • Hi, Yes finally it worked. I was missing one small thing my `Customer.class` was still using the `imports` from `jaxb` instead of `Jakarta`. I was again analysing the whole thing found that. I changed all the imports to `Jakarta` and boom it worked and able to get the `XML` as I expected. Also, the `@XmlPath` is working as expected. – BATMAN_2008 May 12 '21 at 17:50
  • Thanks a lot for your patience and for guiding me through this error. It took my whole day to understand and fix. Finally, at the end of the day, I was able to get it. Thanks to you :) If possible please provide the answer from your post here and maybe some more points so that people can find answers quickly and they do not end up spending so much time. I will accept the answer so you get some reward for your efforts. – BATMAN_2008 May 12 '21 at 17:52
  • 2
    Glad you solved it! Nice! You are welcome to [answer your own question](https://stackoverflow.com/help/self-answer) here to described how you fixed your problem - your own perspective will be helpful. If you want to (and doing this is of course 100% optional), you can vote for the other answer [here](https://stackoverflow.com/a/60285555/12567365) as well. – andrewJames May 12 '21 at 19:00
  • @andrewjames I have one small doubt. Now everything is working fine and able to get my address like this in XML: ` 1 Billing Street `. Now what if I want to skip the `` tag using the `@XmlPath`? I tried to add the following `@XmlPath(".")` and `@XmlPath("./text()")` but both throw the error. I want to add the `street` value directly to the `` so that I can skip the `` tag. I was planning to use the `@XmlPath` for this main reason actually. Is it possible to do using the `@XmlPath`?Thanks – BATMAN_2008 May 13 '21 at 07:06
  • @andrewjames Never mind. I got it. I just added the `@XmlAccessorType(XmlAccessType.FIELD)` to my `Address.class` and that worked so now the `street` tag is directly mapped to `customer` tag and `billing-address` has been skipped. – BATMAN_2008 May 13 '21 at 07:16

1 Answers1

2

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.

BATMAN_2008
  • 2,788
  • 3
  • 31
  • 98