0

I need to pass a namespaceMap to the JSONProvider configuration via web.xml file in Apache CXF.

I do not use Spring on this application and do not do programmatic configuration of Providers like this question suggests: CXF: No message body writer found for class - automatically mapping non-simple resources

Rather use configuration in the style of this non-Spring example from the CXF code.

But, the example does not show how to provide a namespaceMap and I'm unsure of how to specify a map in that style of configuration.

I'm going ahead with some trial-and-error.

Anyone know of a reference guide for the syntax for the JSONProvider config via web.xml?

Community
  • 1
  • 1
Jacob Zwiers
  • 1,092
  • 1
  • 13
  • 33

1 Answers1

0

I was not able to find a reference guide, but did manage to solve the configuration issue by some code spelunking and this mailing list thread from the archives.

For some reason, the suggestion from that mailing list thread didn't work (it was ignoring the custom JSONProvider from the web.xml). I may have had other problems.

Ultimately, I moved away from the idea of web.xml configuration since it was already used to provide a javax.ws.rs.Application and (from looking @ CXF code), it appears that CXF ignore init-param elements from web.xml if it finds an Application.

Further, there doesn't seem be a way of expressing Map types in the web.xml config. That's conjecture based on the thread and looking at the code, so I can't 100% confirm.

This is what my web.xml looks like (same before and after these changes):

<servlet>
    <servlet-name>CXFServlet</servlet-name>
    <display-name>CXF Servlet</display-name>
    <servlet-class>org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>my.javax-ws-rs.Application</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

It was in the my.javax-ws-rs.Application class that I needed to make changes.

Simply added a new JSONProvider to the list of singletons in there:

@Override
public Set< Object> getSingletons() {
    final Set< Object> singletons = new HashSet<>();

   // [SNIP -- existing singletons]
    singletons.add( new my.provider.MyJSONProvider() );

    return singletons;
}

The definition of my.provider.MyJSONProvider is where the rest of the magic happens (yes, it's the "programmatic" configuration that I said I didn't have, but ultimately had to resort to):

@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON}) 
@Provider
public class MyJSONProvider<T> extends JSONProvider<T> {

  public AdministrationUtilisatuerJSONProvider() {

    {
        Map<String, String> newNamespaceMap = new ConcurrentHashMap<>();

        XmlSchema resource1SchemaAnnotation = Resource1.class.getPackage().getAnnotation(javax.xml.bind.annotation.XmlSchema.class);
        String resource1Namespace = resource1SchemaAnnotation.namespace();
        newNamespaceMap.put( resource1Namespace, "resource1JsonPrefix" );

        XmlSchema resource2SchemaAnnotation = Resource2.class.getPackage().getAnnotation(javax.xml.bind.annotation.XmlSchema.class);
        String resource2Namespace = resource2SchemaAnnotation.namespace();
        newNamespaceMap.put( resource2Namespace, "resource2JsonPrefix" );

        setNamespaceMap(newNamespaceMap);
    }

    // Or set this to "true" to ignore all that namespace stuff
    // setIgnoreNamespaces(true);

    // Don't write namespace for default xsi-type elements.
    setWriteXsiType(false);

    // [SNIP] -- Other JSONProvider configuration.  
    //           Check source from CXF, but few comments in code.

  }

One final note about getting Jettison-based JSON working in CXF: you also need the cxf-rt-rs-extension-providers-X.Y.Z.jar on your classpath. This isn't documented in the CXF WHICH_JARS file, but is required.

Jacob Zwiers
  • 1,092
  • 1
  • 13
  • 33