使用 Spring 配置 CXF 以使用 MOXY 进行 XML 编组/解组

发布于 2024-12-28 22:56:47 字数 1852 浏览 0 评论 0原文

我有一个 Java 服务器应用程序,它使用 CXF 提供 SOAP 和 REST Web 服务。目前,它使用 JAX-B 的参考实现进行 XML 编组/解组,但我已将其配置为用 Jackson 替换 Jettison 进行 JSON 编组/解组。我使用 Spring 进行 DI 和应用程序上下文配置。

REST Web 服务配置片段如下所示:

web.xml

<servlet>
    <display-name>Myapp REST Services</display-name>
    <servlet-name>MyappWebServices</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>MyappWebServices</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

applicationContext.xml

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />

<jaxrs:server id="myappCoreSvcRest" address="/rest">
    <jaxrs:serviceBeans>
        <ref bean="fooService" />
        <ref bean="barService" />
    </jaxrs:serviceBeans>

    <jaxrs:providers>
        <ref bean="jsonProvider" />
    </jaxrs:providers>
</jaxrs:server>

此配置有效,并将根据 HTTP Accept 标头返回 XML 或 JSON。我喜欢这个配置的原因是它基于 Spring,并且创建和使用备用 JSON 编码器非常容易。有关配置 CXF 的详细信息,请参阅此处

我的问题是,现在我要提供一个新的(附加的)REST Web 服务,并且我想为这个新的 Web 服务使用不同的 JAX-B XML 绑定。我知道 MOXy 可以做到这一点,但我无法弄清楚如何配置 CXF 端点,以便它将使用 MOXy 进行编组/解组(以及如何告诉 Moxy 我的自定义 XML 映射文件)。我还希望这个新的 Web 服务根据 Accept 标头返回 XML 或 JSON。我还读到 MOXy 2.4+ 也可以处理这个问题!

理想情况下,我可以将 MOXy 用于这个新端点,而不会影响其他现有的 servlet。

I have a Java server application that uses CXF to provide SOAP and REST web services. Currently it uses the reference implementation of JAX-B for XML marshalling/unmarshalling, but I have configured it to replace Jettison with Jackson for JSON marshalling/unmarshalling. I use Spring for DI and application context configuration.

The REST web service configuration snippets looks as follows:

web.xml

<servlet>
    <display-name>Myapp REST Services</display-name>
    <servlet-name>MyappWebServices</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>MyappWebServices</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

applicationContext.xml

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />

<jaxrs:server id="myappCoreSvcRest" address="/rest">
    <jaxrs:serviceBeans>
        <ref bean="fooService" />
        <ref bean="barService" />
    </jaxrs:serviceBeans>

    <jaxrs:providers>
        <ref bean="jsonProvider" />
    </jaxrs:providers>
</jaxrs:server>

This configuration works and will return either XML or JSON depending on the HTTP Accept header. What I like about this configuration is that it is based in Spring and it is super easy to create and use an alternate JSON encoder. Details on configuring CXF can be found here.

My problem is that now I have a new (additional) REST web service to provide and I would like to use a different JAX-B XML binding for this new web service. I understand that MOXy can do this, but I am unable to figure out how to configure a CXF end point so that it will use MOXy for marshalling/unmarshalling (and furthermore how to tell Moxy about my custom XML mapping file). I also would like this new web service to return either XML or JSON depending on the Accept header. I also have read that MOXy 2.4+ can handle that too!

Ideally I could use MOXy for this new endpoint without affecting the other existing servlets.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

邮友 2025-01-04 22:56:47

注意:我是EclipseLink JAXB (MOXy) 的领导者和 JAXB 的成员(JSR-222)专家组。


我暂时不知道 CXF 的确切配置,但下面我提供了一些将 MOXy 与 Spring 结合使用的链接。请随时联系我,我可以帮助你实现这个:

我的问题是,现在我有一个新的(附加的)REST Web 服务
提供,我想为此使用不同的 JAX-B XML 绑定
新的网络服务。我知道 MOXy 可以做到这一点,但我做不到
了解如何配置 CXF 端点以便它将使用
MOXy 用于编组/解编组(以及如何告诉 Moxy
关于我的自定义 XML 映射文件)。

将 MOXy 与 JAX-RS 实现结合使用时,您可以使用 ContextResolver 从 MOXy 的外部映射文件引导:

package blog.bindingfile.jaxrs;

import java.io.*;
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;     
import org.eclipse.persistence.jaxb.JAXBContextFactory;

import blog.bindingfile.Customer;

@Provider
@Produces({"application/xml", "application/json"})
public class CustomerContextResolver implements ContextResolver<JAXBContext> {

    private JAXBContext jc;

    public CustomerContextResolver() {
        ClassLoader cl = Customer.class.getClassLoader();
        InputStream bindings =
            cl.getResourceAsStream("blog/bindingfile/binding.xml");
        try {
            Map<String, Object> props = new HashMap<String, Object>(1);
            props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, bindings);
            jc = JAXBContext.newInstance(new Class[] {Customer.class} , props);
        } catch(JAXBException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                bindings.close();
            } catch(IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public JAXBContext getContext(Class<?> clazz) {
        if(Customer.class == clazz) {
            return jc;
        }
        return null;
    }

} 

对于复杂示例

有关使用 MOXy 与 Spring 的更多信息


我还希望这个新的 Web 服务返回 XML 或 JSON
取决于 Accept 标头。我还读到 MOXy 2.4+ 可以
也处理一下!

是的,JSON 绑定已添加到 EclipseLink 2.4 中。要在您的应用程序中利用此功能,创建 MessageBodyReaderMessageBodyWriter 应该是一个简单的事情:

package org.example;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import javax.xml.transform.stream.StreamSource;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MOXyJSONProvider implements 
    MessageBodyReader<Object>, MessageBodyWriter<Object>{

    @Context
    protected Providers providers;

    public boolean isReadable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public Object readFrom(Class<Object> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException, WebApplicationException {
            try {
                Class<?> domainClass = getDomainClass(genericType);
                Unmarshaller u = getJAXBContext(domainClass, mediaType).createUnmarshaller();
                u.setProperty("eclipselink.media-type", mediaType.toString());
                u.setProperty("eclipselink.json.include-root", false);
                return u.unmarshal(new StreamSource(entityStream), domainClass).getValue();
            } catch(JAXBException jaxbException) {
                throw new WebApplicationException(jaxbException);
            }
    }

    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        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 {
            Class<?> domainClass = getDomainClass(genericType);
            Marshaller m = getJAXBContext(domainClass, mediaType).createMarshaller();
            m.setProperty("eclipselink.media-type", mediaType.toString());
            m.setProperty("eclipselink.json.include-root", false);
            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;
    }

    private JAXBContext getJAXBContext(Class<?> type, MediaType mediaType) 
        throws JAXBException {
        ContextResolver<JAXBContext> resolver 
            = providers.getContextResolver(JAXBContext.class, mediaType);
        JAXBContext jaxbContext;
        if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
            return JAXBContext.newInstance(type);
        } else {
            return jaxbContext;
        }
    }

    private Class<?> getDomainClass(Type genericType) {
        if(genericType instanceof Class) {
            return (Class<?>) genericType;
        } else if(genericType instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
        } else {
            return null;
        }
    }

}

您还可以创建 JSONProvider 的扩展代码>:

了解更多信息

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.


Off hand I do not know the exact config for CXF, but below I have provided some links to using MOXy with Spring. Please feel free to contact me, and I can help you implement this:

My problem is that now I have a new (additional) REST web service to
provide and I would like to use a different JAX-B XML binding for this
new web service. I understand that MOXy can do this, but I am unable
to figure out how to configure a CXF end point so that it will use
MOXy for marshalling/unmarshalling (and furthermore how to tell Moxy
about my custom XML mapping file).

When using MOXy with a JAX-RS implementation you can use a ContextResolver to bootstrap from MOXy's external mapping file:

package blog.bindingfile.jaxrs;

import java.io.*;
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;     
import org.eclipse.persistence.jaxb.JAXBContextFactory;

import blog.bindingfile.Customer;

@Provider
@Produces({"application/xml", "application/json"})
public class CustomerContextResolver implements ContextResolver<JAXBContext> {

    private JAXBContext jc;

    public CustomerContextResolver() {
        ClassLoader cl = Customer.class.getClassLoader();
        InputStream bindings =
            cl.getResourceAsStream("blog/bindingfile/binding.xml");
        try {
            Map<String, Object> props = new HashMap<String, Object>(1);
            props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, bindings);
            jc = JAXBContext.newInstance(new Class[] {Customer.class} , props);
        } catch(JAXBException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                bindings.close();
            } catch(IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public JAXBContext getContext(Class<?> clazz) {
        if(Customer.class == clazz) {
            return jc;
        }
        return null;
    }

} 

For a Complext Example

For More Information on Using MOXy with Spring


I also would like this new web service to return either XML or JSON
depending on the Accept header. I also have read that MOXy 2.4+ can
handle that too!

Yes, JSON binding is being added to EclipseLink 2.4. To leverage this in your application it should be a simple matter of creating a MessageBodyReader and a MessageBodyWriter:

package org.example;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import javax.xml.transform.stream.StreamSource;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MOXyJSONProvider implements 
    MessageBodyReader<Object>, MessageBodyWriter<Object>{

    @Context
    protected Providers providers;

    public boolean isReadable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    public Object readFrom(Class<Object> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException, WebApplicationException {
            try {
                Class<?> domainClass = getDomainClass(genericType);
                Unmarshaller u = getJAXBContext(domainClass, mediaType).createUnmarshaller();
                u.setProperty("eclipselink.media-type", mediaType.toString());
                u.setProperty("eclipselink.json.include-root", false);
                return u.unmarshal(new StreamSource(entityStream), domainClass).getValue();
            } catch(JAXBException jaxbException) {
                throw new WebApplicationException(jaxbException);
            }
    }

    public boolean isWriteable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
        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 {
            Class<?> domainClass = getDomainClass(genericType);
            Marshaller m = getJAXBContext(domainClass, mediaType).createMarshaller();
            m.setProperty("eclipselink.media-type", mediaType.toString());
            m.setProperty("eclipselink.json.include-root", false);
            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;
    }

    private JAXBContext getJAXBContext(Class<?> type, MediaType mediaType) 
        throws JAXBException {
        ContextResolver<JAXBContext> resolver 
            = providers.getContextResolver(JAXBContext.class, mediaType);
        JAXBContext jaxbContext;
        if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
            return JAXBContext.newInstance(type);
        } else {
            return jaxbContext;
        }
    }

    private Class<?> getDomainClass(Type genericType) {
        if(genericType instanceof Class) {
            return (Class<?>) genericType;
        } else if(genericType instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
        } else {
            return null;
        }
    }

}

You may also be able to create an extension of JSONProvider:

For More Information

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文