Previously I was using the Eclipselink Moxy NAMESPACE_PREFIX_MAPPER to provide the custom namespaces to my JaxbContext and Marshaller and everything was working as expected. But I want to move away from the Moxy and use JAXB RI so changed everything.
The only problem that I am facing is related to providing the MarshallerProperties and JAXBContextProperties. I changed them to use from glassfish as per the answer mentioned here and the documentation but after the change I am facing 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 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);
}
}
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-core</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-xjc</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
For jaxb-ri, the property should be
org.glassfish.jaxb.namespacePrefixMapperand the value with it should be of typeorg.glassfish.jaxb.runtime.marshaller.NamespacePrefixMapperIf not, you'll see the exception you've seen before (property not handled or must be of type ...)
Source here
EDIT
In addition to my previous first answer, you should pass the namespaceMapper property in JAXBContext creation.
See supported properties during JAXBContext phase in the following method :
org.glassfish.jaxb.runtime.v2.ContextFactory#createContext(java.lang.Class[], java.util.Map<java.lang.String,java.lang.Object>)You should create your JAXBContext with your current commented line of code :
If doing so and adding the
namespacetoChild1class as@XmlRootElement(name = "Child1", namespace = "https://example1.com/"), you'll get the following output :