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

Custom namespace marshalling using JAXB RI results in property "org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper" is not supported #1795

Open
Aravinda93 opened this issue Mar 8, 2024 · 3 comments

Comments

@Aravinda93
Copy link

Aravinda93 commented Mar 8, 2024

For convience I have added the sample code repo here: https://github.com/Aravinda93/test/tree/main

I am using JAXB RI to marshall the Java objects into XML but during the marshalling I need to provide some of the custom namespaces to my Marshaller and JAXBContext. I referred the documentation and answer here to do the same but its resulting in the error:

Exception in thread "main" jakarta.xml.bind.JAXBException: property "org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper" is not supported
	at org.glassfish.jaxb.runtime.v2.ContextFactory.createContext(ContextFactory.java:126)
	at org.glassfish.jaxb.runtime.v2.ContextFactory.createContext(ContextFactory.java:246)
	at org.glassfish.jaxb.runtime.v2.JAXBContextFactory.createContext(JAXBContextFactory.java:58)
	at jakarta.xml.bind.ContextFinder.find(ContextFinder.java:324)
	at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:392)
	at io.test.convert.MainXML.main(MainXML.java:27)

Following is the completed code I have:
Child1.class:

package io.test.convert;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

@XmlRootElement(name = "Child1")
@XmlType(
        name = "Child1",
        propOrder = {
                "name",
                "age",
                "originalName"
        },
        factoryClass = ObjectFactory.class,
        factoryMethod = "createChild1")
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@XmlAccessorType(XmlAccessType.FIELD)
public class Child1 extends Parent {
    private String originalName;
}

Parent.class:

package io.test.convert;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlTransient;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@XmlTransient
@Builder
public class Parent implements Serializable {
    @XmlTransient
    private String type;
    private String name;
    private String age;
}

CustomNamespacePrefixMapper.class:

package io.test.convert;

import org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper;
import java.util.HashMap;
import java.util.Map;

public class CustomNamespacePrefixMapper extends NamespacePrefixMapper {
    public static final Map<String, String> NAMESPACE_MAP = Map.of(
            "http://www.w3.org/2001/XMLSchema-instance", "xsi",
            "https://example1.com/", "example1",
            "https://example2.com/", "example2");

    private Map<String, String> namespaceMap;
    public CustomNamespacePrefixMapper(final Map<String, String> namespaceMap) {
        this.namespaceMap = namespaceMap;
    }
    public CustomNamespacePrefixMapper() {
        this(new HashMap<>(NAMESPACE_MAP));
    }
    @Override
    public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
        return namespaceMap.getOrDefault(namespaceUri, suggestion);
    }
}

MainXML.class:

package io.test.convert;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.UnmarshalException;
import jakarta.xml.bind.Unmarshaller;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

public class MainXML {
    public static void main(String[] args) throws Exception {
        final Map<String, String> myNamespaces = new HashMap<>();
        myNamespaces.put("test", "https://test.com");
        myNamespaces.put("test2", "https://test2.com");

        final InputStream xmlInputStream = MainXML.class.getResourceAsStream("/SampleXML.xml");

        final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
        inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
        final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(xmlInputStream);

        final JAXBContext jaxbContext = JAXBContext.newInstance("io.test.convert", Thread.currentThread().getContextClassLoader(),
                new HashMap<>() {
                    {
                        put(
                                "org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper",
                                new CustomNamespacePrefixMapper());
                    }
                });
       //final JAXBContext jaxbContext = JAXBContext.newInstance("io.test.convert", Thread.currentThread().getContextClassLoader());
        final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        xmlStreamReader.next();

        final Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty("org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper", new CustomNamespacePrefixMapper());

        try {
            while (xmlStreamReader.hasNext()) {
                Object event = unmarshaller.unmarshal(xmlStreamReader, Child1.class).getValue();
                if (event instanceof Child1) {
                    Child1 child1 = (Child1) event;
                    marshaller.marshal(child1, System.out);
                }
            }
        } catch (UnmarshalException e) {
            // Handle any unmarshalling exceptions
            e.printStackTrace();
        }
    }
}

Following is my sampleXML.xml:

<Child1>
    <type>ChildType</type>
    <name>Batman</name>
    <age>30</age>
    <originalName>Bruce</originalName>
</Child1>

ObjectFactory.class:

package io.test.convert;

import jakarta.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public final class ObjectFactory {
    private ObjectFactory() {}
    public static Child1 createChild1() {
        return new Child1();
    }
}

When I run I get the error:

Exception in thread "main" jakarta.xml.bind.JAXBException: property "org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper" is not supported
	

How to provide the custom namespaces to the JAXBContext/Marshaller so it can be used during the marshalling of the Java objects to XML?

Following are my dependencies:

  <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
            <version>4.0.1</version>
        </dependency>
    </dependencies>
@antoniosanct
Copy link
Contributor

@Aravinda93

  1. The correct name of the property is "org.glassfish.jaxb.namespacePrefixMapper".
  2. This property is exclusive for marshalling. You can not use at unmarshalling process.

Regards,
Antonio.

@Aravinda93
Copy link
Author

@antoniosanct Thanks a lot for the response. For your convience I have added the sample code repo here: https://github.com/Aravinda93/test/tree/main

I was trying couple of things thats the reason I added the org.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapper but I have tried the org.glassfish.jaxb.namespacePrefixMapper and that did not work either for me.

As you mentioned I have removed the Unmarshaller part although I was not using it and kept only the Marshaller but still I get the same error:

Exception in thread "main" jakarta.xml.bind.JAXBException: property "org.glassfish.jaxb.namespacePrefixMapper" is not supported

Following is my main class updated:

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Marshaller;

import java.util.HashMap;
import java.util.Map;

public class MainXML {
    public static void main(String[] args) throws Exception {
        final Map<String, String> myNamespaces = new HashMap<>();
        myNamespaces.put("test", "https://test.com");
        myNamespaces.put("test2", "https://test2.com");

        final JAXBContext jaxbContext = JAXBContext.newInstance("io.test.convert", Thread.currentThread().getContextClassLoader(),
                new HashMap<>() {
                    {
                        put(
                                "org.glassfish.jaxb.namespacePrefixMapper",
                                new CustomNamespacePrefixMapper());
                    }
                });
        //final JAXBContext jaxbContext = JAXBContext.newInstance("io.test.convert", Thread.currentThread().getContextClassLoader());

        final Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty("org.glassfish.jaxb.namespacePrefixMapper", new CustomNamespacePrefixMapper());

        final Child1 child1 = new Child1();
        child1.setName("Batman");
        child1.setAge("30");
        child1.setType("Superhero");
        child1.setOriginalName("Bruce Wayne");

        marshaller.marshal(child1, System.out);
    }
}

@antoniosanct
Copy link
Contributor

@Aravinda93
You can not set this property at context initialization. See constants defined here to check what properties you can initialize. Lines 23 and 28 of your class are correct (delete hashmap initialization in 15 and uncomment 23 to run fine).

Regards,
Antonio.

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

No branches or pull requests

2 participants