2012-01-20 168 views
4

我们在我们的Web API中返回的模型对象上使用了JAXB Annotations,我们希望数据能够根据用户偏好进行本地化和其他值格式化(即度量与法规)。我们通过向Marshaller添加自定义适配器来实现这一点。我如何使用JAXB JAXB注释的响应

Marshaller marshaller = jc.createMarshaller(); 
marshaller.setAdapter(NumberPersonalizedXmlAdapter.class, 
      new NumberPersonalizedXmlAdapter(locale); 
marshaller.marshal(expected, writer); 

尝试最简单的办法我只得到HTTP请求头的语言环境,并在MessageBodyWriter类之一提供给编组器。

我着眼于扩展XMLRootElementProvider之类的默认注册提供程序,但意识到它们大部分都是最终编写的,所以我放弃了这种方法。至少会有10个班级需要延长,所以这并不理想。

有谁知道如何让MessageBodyWriter中的编码器为每个请求设置客户适配器?我很确定它与ContextResolver有关。

回答

4

编写一个用于编组的ContextResolver会比编写MessageBodyWriter更清洁和更合适的解决方案。所有JAXB类都使用Providers.getContextResolver方法来获得编组。我提供了我自定义的ContextResolver,并且我有国际化的响应。

@Provider 
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) 
public class JaxbPersonalizerContextResolver implements ContextResolver<Marshaller> { 

    private HttpHeaders requestHeaders; 

    public JaxbPersonalizerContextResolver(@Context HttpHeaders requestHeaders) { 
     this.requestHeaders = requestHeaders; 
    } 

    @Override 
    public Marshaller getContext(Class<?> type) { 
     Locale locale = If.first(this.requestHeaders. 
            getAcceptableLanguages(), Locale.US); 
     NumberFormat formatter = NumberFormat.getNumberInstance(locale); 
     formatter.setMaximumFractionDigits(1); 

     Marshaller marshaller; 
     try { 
      JAXBContext jc = JAXBContext.newInstance(type); 
      marshaller = jc.createMarshaller(); 
     } catch (JAXBException e) { 
      throw new RuntimeException(e); 
     } 
     marshaller.setAdapter(QuantityXmlAdapter.class, 
          new QuantityXmlAdapter.Builder().locale(locale).build()); 
     marshaller.setAdapter(NumberPersonalizedXmlAdapter.class, 
       new NumberPersonalizedXmlAdapter.Builder(). 
            formatter(formatter).build()); 
     return marshaller; 
    } 
} 

的JSON没有被本地化,经过一番调查后,我意识到杰克逊JSON库正在代替JAXBJSONElementProvider与Jersey库分布。我在web.xml中使用了removed the POJOMappingFeature configuration,并且我有本地化的JSON,但是它不如Jackson JSON。

一个非常干净的解决方案,使我认为JAX-RS和Jersey实现非常完美。

0

A MessageBodyWriter是此用例的正确方法。我建议加入以下字段添加到您的MessageBodyWriter

@javax.ws.rs.core.Context 
protected Providers providers; 

,然后用它来访问JAXBContext来创建Marshaller

public void writeTo(DataObject dataObject, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> multivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException { 
    JAXBContext jaxbContext = null; 
    ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, arg3); 
    if(null != resolver) { 
     jaxbContext = resolver.getContext(type); 
    } 
    if(null == jaxbContext) { 
     jaxbContext = JAXBContext.newInstance(type); 
    } 
    Marshaller marshaller = jaxbContext.createMarshaller(); 
} 

相关的例子

1

我解决了写我自己的MessageBodyWriter的问题,它收到注入到构造函数中的HttpHeaders,稍后我在写回应时使用该构造函数。我会包括整个班级,因为它不是那么大。

@Produces(MediaType.APPLICATION_XML) 
@Provider 
public class JaxbPersonalizationProvider implements MessageBodyWriter<Object> { 

    private HttpHeaders requestHeaders; 
    private Providers providers; 

    public JaxbPersonalizationProvider(@Context HttpHeaders requestHeaders, @Context Providers providers) { 
     this.requestHeaders = requestHeaders; 
     this.providers = providers; 
    } 

    @Override 
    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { 
     return type.getAnnotation(XmlRootElement.class) != null && mediaType.equals(MediaType.APPLICATION_XML_TYPE); 
    } 

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

    @Override 
    public void writeTo(Object t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, 
      MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, 
      WebApplicationException { 
     Locale locale = If.first(this.requestHeaders.getAcceptableLanguages(), Locale.US); 
     NumberFormat formatter = NumberFormat.getNumberInstance(locale); 
     formatter.setMaximumFractionDigits(1); 

     Marshaller marshaller; 
     try { 
      JAXBContext jc = JAXBContext.newInstance(TrackInfo.class); 
      marshaller = jc.createMarshaller(); 
      marshaller.setAdapter(QuantityXmlAdapter.class, new QuantityXmlAdapter.Builder().locale(locale).build()); 
      marshaller.setAdapter(NumberPersonalizedXmlAdapter.class, new NumberPersonalizedXmlAdapter.Builder() 
        .formatter(formatter).build()); 

      marshaller.marshal(t, entityStream); 
     } catch (JAXBException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

与的EN-美国默认的语言环境产生这个XML摘录:

<display lang="en_US"> 
    <value>3,286.1</value> 
</display> 

这个XML片段时,FR-FR的语言环境中的头被发送:

<display lang="fr_FR"> 
    <value>3 286,1</value> 
</display> 

这种方法仍然不理想,因为我现在需要为JSON编写类似的MessageBodyWriter,或者为此MessageBodyWriter添加JSON支持。此外,我假设默认的JAXB提供者正在做一些调整,我没有利用。