namespace prefix for generated jersey rest respons

2019-09-11 08:49发布

问题:

I'm generating rest responses (using Jersey) from jaxb models. And for some of the responses, the generated XML has namespace prefix (ns2) added to the namespace attribute although they all exists in the same namespace. But for others, it is perfectly fine.

With my analysis, I think it happens when there is a complex element (another jaxb model) is being used inside one. But all these models are declared in same namespace in package-info.java.

Here is the code.

XYZModel.class

package int.xyxp.model;

@XmlType(name="xyztype")
@XmlRootElement(name="xyz")
@XmlSeeAlso({XModel.class, YModel.class, Z.class})
@XmlAccessorType(XmlAccessType.FIELD)
public class XYZModel extends VModel {

    @XmlElement(name="code")
    private String code;

    @XmlElementWrapper(name="refs",  namespace="http://reference.com/ref")
    @XmlElementRef
    private List<XModel> refs = new ArrayList<XModel>(0); 
//continues

package-info.java

@javax.xml.bind.annotation.XmlSchema( 
    namespace = "http://reference.com/ref",    
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
package int.xyxp.model;

generated XML

<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<ns2:xyz version="1.0" xmlns:ns2="http://reference.com/ref">
    <ns2:code>15</ns2:code>
    <ns2:refs/>
</ns2:xyz>

expected XML (without prefix, by assuming default namespace).

<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<xyz version="1.0" xmlns="http://reference.com/ref">
    <code>15</code>
    <refs/>
</xyz>

any thoughts. Thanks.

[EDIT]

After I tried to insert my preferred namespace prefix and it doesn't work even. so probably the package-info.java is used only for namespace and not for selecting the namespace prefix.

package-info.java

 @javax.xml.bind.annotation.XmlSchema( 
           namespace = "http://reference.com/ref",    
    xmlns = { 
      @javax.xml.bind.annotation.XmlNs(prefix = "ref", namespaceURI = "http://reference.com/ref"), 
    }, 
    elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
 package int.xyxp.model;

回答1:

NOTE: I have overridden MessageBodyWriter to provide my own namespace ("my"). Even though I have returned empty "", it takes ns2 by default when its empty. So this answers works if you want to have your own namespace instead of default "ns2".

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;

@Produces(value=MediaType.APPLICATION_XML)
public class WSNamespaceWriter implements MessageBodyWriter<Object>{

    @Context
    protected Providers providers;

    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        System.out.println("Calling MessageWriter writetable--> " + type.getName());
        return true;
    }

    public void writeTo(Object object, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders,
        OutputStream entityStream) throws IOException,
        WebApplicationException {
        try {
            System.out.println("Calling MessageWriter-->");
            ContextResolver<JAXBContext> resolver 
                = providers.getContextResolver(JAXBContext.class, mediaType);
            JAXBContext jaxbContext;
            if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
                jaxbContext = JAXBContext.newInstance(type);
            }
            Marshaller m = jaxbContext.createMarshaller();
            NamespacePrefixMapper mapper = new NamespacePrefixMapper() {
                public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
                    System.out.println ("Called NAMESPACE----------" + namespaceUri);
                    if ("http://www.example.com".equals(namespaceUri)
                            || ("").equals(namespaceUri)) {
                        System.out.println ("Called NAMESPACE  return --------");
                        return "my"; // my own namespace
                    }   
                    System.out.println ("Called NAMESPACE  return ns--------");
                    return "";
                }
            };
            m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);           
            m.marshal(object, entityStream);
        } catch(JAXBException jaxbException) {
            throw new WebApplicationException(jaxbException);
        }
    }

    public long getSize(Object t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

}