调查
环境:Jersey 2.13(所有提供程序版本也是 2.13)。
无论您使用声明式链接还是程序式链接,序列化都不应有所不同。我选择了程序化,只是因为 我可以 :-)
测试类:
@XmlRootElement
public class TestClass {
private javax.ws.rs.core.Link link;
public void setLink(Link link) { this.link = link; }
@XmlElement(name = "link")
@XmlJavaTypeAdapter(Link.JaxbAdapter.class)
public Link getLink() { return link; }
}
@Path("/links")
public class LinkResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getResponse() {
URI uri = URI.create("https://stackoverflow.com/questions/24968448");
Link link = Link.fromUri(uri).rel("stackoverflow").build();
TestClass test = new TestClass();
test.setLink(link);
return Response.ok(test).build();
}
}
@Test
public void testGetIt() {
WebTarget baseTarget = target.path("links");
String json = baseTarget.request().accept(
MediaType.APPLICATION_JSON).get(String.class);
System.out.println(json);
}
不同 Provider 的结果(无需额外配置)
jersey-media-moxy
依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
结果(奇怪)
{
"link": "javax.ws.rs.core.Link$JaxbLink@cce17d1b"
}
jersey-media-json-jackson
依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
结果(关闭,但 params 是什么?)
{
"link": {
"params": {
"rel": "stackoverflow"
},
"href": "https://stackoverflow.com/questions/24968448"
}
}
jackson-jaxrs-json-provider
依赖
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
</dependency>
结果(两个不同的结果,两个不同的 JSON 提供程序)
resourceConfig.register(JacksonJsonProvider.class);
{
"link": {
"uri": "https://stackoverflow.com/questions/24968448",
"params": {
"rel": "stackoverflow"
},
"type": null,
"uriBuilder": {
"absolute": true
},
"rels": ["stackoverflow"],
"title": null,
"rel": "stackoverflow"
}
}
resourceConfig.register(JacksonJaxbJsonProvider.class);
{
"link": {
"params": {
"rel": "stackoverflow"
},
"href": "https://stackoverflow.com/questions/24968448"
}
}
我的结论
我们使用@XmlJavaTypeAdapter(Link.JaxbAdapter.class) 注释该字段。让我们看一下这个适配器的sn-p
public static class JaxbAdapter extends XmlAdapter<JaxbLink, Link> {...}
所以从Link,我们被编组到JaxbLink
public static class JaxbLink {
private URI uri;
private Map<QName, Object> params;
...
}
jersey-media-moxy
似乎是一个错误...请参阅下面的解决方案。
其他人
另外两个依赖jackson-module-jaxb-annotations 来处理使用 JAXB 注释的编组。 jersey-media-json-jackson 将自动注册所需的JaxbAnnotationModule。对于jackson-jaxrs-json-provider,使用JacksonJsonProvider 将不支持JAXB 注解(无需配置),使用JacksonJsonJaxbProvider 将为我们提供JAXB 注解支持。
所以如果我们有 JAXB 注释支持,我们将被编组到JaxbLink,这将给出这个结果
{
"link": {
"params": {
"rel": "stackoverflow"
},
"href": "https://stackoverflow.com/questions/24968448"
}
}
我们可以通过所有不需要的属性获得结果的方法是1),使用jackson-jaxrs-json-provider 的JacksonJsonProvider 或2),创建ContextResolver 代表ObjectMapper,我们不注册JaxbAnnotationModule。你似乎正在做其中之一。
解决方案
以上内容仍然没有让我们到达我们想要到达的地方(即没有params)。
对于jersey-media-json-jackson 和jackson-jaxrs-json-provider...
...使用Jackson,此时我唯一能想到的就是创建一个自定义序列化程序
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import javax.ws.rs.core.Link;
public class LinkSerializer extends JsonSerializer<Link>{
@Override
public void serialize(Link link, JsonGenerator jg, SerializerProvider sp)
throws IOException, JsonProcessingException {
jg.writeStartObject();
jg.writeStringField("rel", link.getRel());
jg.writeStringField("href", link.getUri().toString());
jg.writeEndObject();
}
}
然后为ObjectMapper创建一个ContextResolver,我们在其中注册序列化器
@Provider
public class ObjectMapperContextResolver
implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Link.class, new LinkSerializer());
mapper.registerModule(simpleModule);
}
@Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
这是结果
{
"link": {
"rel": "stackoverflow",
"href": "https://stackoverflow.com/questions/24968448"
}
}
使用 jersey-media-moxy,似乎有一个 Bug 缺少 JaxbLink 类中的设置器,因此编组恢复为调用 toString,如上所示。正如here by Garard Davidson 提议的那样,解决方法就是创建另一个适配器
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;
public class LinkAdapter
extends XmlAdapter<LinkJaxb, Link> {
public LinkAdapter() {
}
public Link unmarshal(LinkJaxb p1) {
Link.Builder builder = Link.fromUri(p1.getUri());
for (Map.Entry<QName, Object> entry : p1.getParams().entrySet()) {
builder.param(entry.getKey().getLocalPart(), entry.getValue().toString());
}
return builder.build();
}
public LinkJaxb marshal(Link p1) {
Map<QName, Object> params = new HashMap<>();
for (Map.Entry<String,String> entry : p1.getParams().entrySet()) {
params.put(new QName("", entry.getKey()), entry.getValue());
}
return new LinkJaxb(p1.getUri(), params);
}
}
class LinkJaxb {
private URI uri;
private Map<QName, Object> params;
public LinkJaxb() {
this (null, null);
}
public LinkJaxb(URI uri) {
this(uri, null);
}
public LinkJaxb(URI uri, Map<QName, Object> map) {
this.uri = uri;
this.params = map!=null ? map : new HashMap<QName, Object>();
}
@XmlAttribute(name = "href")
public URI getUri() {
return uri;
}
@XmlAnyAttribute
public Map<QName, Object> getParams() {
return params;
}
public void setUri(URI uri) {
this.uri = uri;
}
public void setParams(Map<QName, Object> params) {
this.params = params;
}
}
改用这个适配器
@XmlElement(name = "link")
@XmlJavaTypeAdapter(LinkAdapter.class)
private Link link;
会给我们想要的输出
{
"link": {
"href": "https://stackoverflow.com/questions/24968448",
"rel": "stackoverflow"
}
}
更新
现在我想起来了,LinkAdapter 也可以与 Jackson 提供者一起使用。无需创建 Jackson Serializer/Deserializer。假设启用了JacksonFeature,Jackson 模块应该已经支持 JAXB 注释。上面的示例分别显示了使用 JAXB/JSON 提供程序,但假设仅启用了 JacksonFeature,则应使用提供程序的 JAXB 版本。这实际上可能是更优选的解决方案。无需为ObjectMapper 创建ContextResolvers :-D
也可以在包级别声明注解,见here