我们正在从我们的Web API返回的模型对象上使用JAXB Annotations,我们希望数据被本地化,并且其他值根据用户首选项(即度量与法规)进行格式化。我们通过向Marshaller添加自定义适配器来实现此目的。
Marshaller marshaller = jc.createMarshaller();
marshaller.setAdapter(NumberPersonalizedXmlAdapter.class,
new NumberPersonalizedXmlAdapter(locale);
marshaller.marshal(expected, writer);
尝试最简单的方法我将从HTTP请求标头中获取Locale,并将其提供给MessageBodyWriter类之一的Marshaller。
我考虑扩展默认注册的提供程序,如XMLRootElementProvider,但意识到它们大部分是最终的,所以我放弃了这种方法。无论如何我至少需要延伸10个课程,因此这并不理想。
有没有人知道如何最好地在MessageBodyWriter中为每个请求设置客户适配器的marshaller?我很确定它与ContextResolver有关。
答案 0 :(得分:4)
为编组编写ContextResolver会产生比编写MessageBodyWriter更清晰,更合适的解决方案。所有JAXB类都使用Providers.getContextResolver方法来获取编组器。我提供自定义ContextResolver,我有i18n回复。
@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没有被本地化,经过一些调查后,我意识到正在使用Jackson JSON库而不是与Jersey库一起分发的JAXBJSONElementProvider。我在web.xml中removed the POJOMappingFeature configuration并且我已经本地化了JSON,但是它不如Jackson JSON那么好。
一个非常干净的解决方案让我觉得JAX-RS和Jersey的实现做得很好。
答案 1 :(得分: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-US生成此xml摘录:
<display lang="en_US">
<value>3,286.1</value>
</display>
以及在标题中发送fr-FR的语言环境时的此xml摘录:
<display lang="fr_FR">
<value>3 286,1</value>
</display>
这种方法仍然不理想,因为我现在需要为JSON编写类似的MessageBodyWriter或为此MessageBodyWriter添加JSON支持。另外我假设默认的JAXB提供程序正在做一些我没有利用的调整。
答案 2 :(得分:0)
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();
}
相关示例