Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] JAXB2 Namespace Prefix Plugin creating duplicated package info namespace #577

Open
mathisgauthey opened this issue Nov 22, 2024 · 5 comments
Labels

Comments

@mathisgauthey
Copy link

mathisgauthey commented Nov 22, 2024

Hey there.

My usage of jaxb-tools

I'm using jaxb-tools in a factur-x API for :

  • Generating classes from xsd files using xjc and your maven plugin
  • Mapping appropriate namespaces to the XML tags when marshalling my object into a xml string

Context

Basically, my process involves :

  1. Sending a json file with the appropriate serialized object along with a pdf
  2. The json is mapped into an object from the jaxb-generated classes
  3. I then marshal this object into a proper xml file with the correct namespaces
  4. I incorporate the xml into the pdf to create a factur-x file

My issue

My issue is related to namespaces associations in the package-info file.

I don't understand why my generated package-info would be different if my executions in the pom.xml file are identical, same thing for the bindings.xjb files. But here they are :

image

Hence, the minimal profile is working fine with proper generation of factur-x compliant file.

The 4 other profiles don't work well at all.

Reproducible example

Here is my plugin config inside my pom.xml :

<plugin>
                <groupId>org.jvnet.jaxb</groupId>
                <artifactId>jaxb-maven-plugin</artifactId>
                <version>4.0.8</version>
                <configuration>
                    <extension>true</extension>
                    <args>
                        <arg>-Xnamespace-prefix</arg>
                    </args>
                    <plugins>
                        <plugin>
                            <groupId>org.jvnet.jaxb</groupId>
                            <artifactId>jaxb-plugins</artifactId>
                            <version>4.0.8</version>
                        </plugin>
                    </plugins>
                </configuration>
                <executions>
                    <execution>
                        <id>xjc-minimum</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaDirectory>${project.basedir}/src/main/resources/schemas/minimum</schemaDirectory>
                            <generateDirectory>${project.build.directory}/generated-sources/jaxb/minimum</generateDirectory>
                            <generatePackage>REDACTED.entity.generated.minimum</generatePackage>
                            <cleanPackageDirectories>false</cleanPackageDirectories>
                        </configuration>
                    </execution>
                    <execution>
                        <id>xjc-basicwl</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaDirectory>${project.basedir}/src/main/resources/schemas/basicwl</schemaDirectory>
                            <generateDirectory>${project.build.directory}/generated-sources/jaxb/basicwl</generateDirectory>
                            <generatePackage>REDACTED.entity.generated.basicwl</generatePackage>
                        </configuration>
                    </execution>
                    <execution>
                        <id>xjc-basic</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaDirectory>${project.basedir}/src/main/resources/schemas/basic</schemaDirectory>
                            <generateDirectory>${project.build.directory}/generated-sources/jaxb/basic</generateDirectory>
                            <generatePackage>REDACTED.entity.generated.basic</generatePackage>
                        </configuration>
                    </execution>
                    <execution>
                        <id>xjc-en16931</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaDirectory>${project.basedir}/src/main/resources/schemas/en16931</schemaDirectory>
                            <generateDirectory>${project.build.directory}/generated-sources/jaxb/en16931</generateDirectory>
                            <generatePackage>REDACTED.entity.generated.en16931</generatePackage>
                        </configuration>
                    </execution>
                    <execution>
                        <id>xjc-extended</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaDirectory>${project.basedir}/src/main/resources/schemas/extended</schemaDirectory>
                            <generateDirectory>${project.build.directory}/generated-sources/jaxb/extended</generateDirectory>
                            <generatePackage>REDACTED.entity.generated.extended</generatePackage>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Here you can find the approporiate filestructure using the xsd files from fnfe-mpe.org zip :

image

And finally, here is a bindings.xjb file example, only the schemaLocation is changing :

<?xml version="1.0"?>
<jxb:bindings xmlns:jxb="https://jakarta.ee/xml/ns/jaxb"
              xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
              xmlns:namespace="urn:jaxb.jvnet.org:plugin:namespace-prefix"
              version="3.0">
    <jxb:globalBindings>
        <xjc:simple/>
    </jxb:globalBindings>
    <jxb:bindings schemaLocation="Factur-X_1.0.07_MINIMUM.xsd">
        <jxb:schemaBindings>
            <jxb:package name="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"/>
        </jxb:schemaBindings>
        <jxb:bindings>
            <namespace:prefix name="rsm"/>
            <namespace:prefix name="ram" namespaceURI="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"/>
            <namespace:prefix name="udt" namespaceURI="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"/>
            <namespace:prefix name="qdt" namespaceURI="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"/>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

Note that the globalBindings part is used for defining the XmlRootElement that would otherwise be missing when marshalling to xml :

jakarta.xml.bind.MarshalException<EOL> - with linked exception:<EOL>[com.sun.istack.SAXException2: unable to marshal type "REDACTED.entity.generated.extended.CrossIndustryInvoiceType" as an element because it is missing an @XmlRootElement annotation

Questions

  • Is there anything I'm doing that is wrong ? I have read extensively your documentation and stack overflow posts, but I might have missed something
  • Otherwise, maybe something is wrong in the librairy handling of multiple executions. Maybe I can help finding a fix ?

Thanks in advance and have a great day !

@laurentschoelens
Copy link
Collaborator

Hi @mathisgauthey

Thanks for your detailed issue.

We'll check soon if it is a bug or a configuration problem.

Regards

@laurentschoelens
Copy link
Collaborator

Hi @mathisgauthey : you're mentionning "fnfe-mpe.org zip" but I can't find the mentionned zip file above. Could you share it please as MRE ?
Thanks 😄

@mathisgauthey
Copy link
Author

Hi @mathisgauthey : you're mentionning "fnfe-mpe.org zip" but I can't find the mentionned zip file above. Could you share it please as MRE ? Thanks 😄

Hey there, I just downloaded it in french and remove the unwanted files so that it fits the 25mo Github limit.

You'll find xsd files along with factur-x PDF examples.

factur-x.zip

@mathisgauthey
Copy link
Author

mathisgauthey commented Nov 25, 2024

I happened to find a workaround that could help locate the problem.

Removing this part from the binding files :

<jxb:globalBindings>
        <xjc:simple/>
    </jxb:globalBindings>

It allows me to use CrossIndustryInvoiceType.java instead of CrossIndustryInvoice.java and it removed the unusual duplicated package-info :

//
// This file was generated by the Eclipse Implementation of JAXB, v4.0.5 
// See https://eclipse-ee4j.github.io/jaxb-ri 
// Any modifications to this file will be lost upon recompilation of the source schema. 
//

@jakarta.xml.bind.annotation.XmlSchema(namespace = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100", elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED, xmlns = {
    @jakarta.xml.bind.annotation.XmlNs(namespaceURI = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100", prefix = "ram"),
    @jakarta.xml.bind.annotation.XmlNs(namespaceURI = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100", prefix = "udt"),
    @jakarta.xml.bind.annotation.XmlNs(namespaceURI = "urn:un:unece:uncefact:data:standard:QualifiedDataType:100", prefix = "qdt"),
    @jakarta.xml.bind.annotation.XmlNs(namespaceURI = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100", prefix = "rsm")
})
package REDACTED.entity.generated.extended;

And then, the only issue I had left was related to using an Object of CrossIndustryInvoiceType : There was an error with a missing @XmlRootElement which I solved by marshalling differently :

Old :

private static String getXmlString(Object invoice) throws JsonProcessingException {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(invoice.getClass());
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            StringWriter stringWriter = new StringWriter();
            marshaller.marshal(invoice, stringWriter);
            return stringWriter.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

New :

private static <T> String getXmlString(T invoice) throws JsonProcessingException {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(invoice.getClass());
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            StringWriter stringWriter = new StringWriter();
            marshaller.marshal(new JAXBElement<>(new QName("urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100", "CrossIndustryInvoice", "rsm"), (Class<T>) invoice.getClass(), invoice), stringWriter);
            return stringWriter.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }

It now works fine, I needed to modify some of my jsons input files because removing the part from the bindings file renamed some attributes from plural to strictly singular. But it now works fine !

@laurentschoelens
Copy link
Collaborator

That's good news 😄

You can manually add the @XmlRootElement to the CrossIndustryInvoiceType by using the jaxb-annotate-plugin with appropriate binding file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants