gpt4 book ai didi

java - JAX-RS HATEOAS 使用 Jersey,JSON 中不需要的链接属性

转载 作者:IT老高 更新时间:2023-10-28 20:47:06 26 4
gpt4 key购买 nike

从 Jersey 2.9 开始,可以通过声明式链接为超媒体驱动的 REST API 创建链接关系。

这段代码,例如:

@InjectLink(
resource = ItemResource.class,
style = Style.ABSOLUTE,
bindings = @Binding(name = "id", value = "${instance.id}"),
rel = "self"
)
@XmlJavaTypeAdapter(Link.JaxbAdapter.class)
@XmlElement(name="link")
Link self;

...理论上预计会产生这样的 JSON:
"link" : {
"rel" : "self",
"href" : "http://localhost/api/resource/1"
}

但是,Jersey 生成了不同的 JSON,其中包含许多我不需要的属性:
"link" : {
"rel" : "self",
"uri" : "http://localhost/api/resource/1",
"type": null,
"uriBuilder" : null
}

另请注意,不是 href ,它使用 uri .我查看了 Jersey 对 Link 的实现对象并找到 JerseyLink .

我想使用 Jersey 的声明式链接而不是推出我自己的实现。我最终使用 Jackson 注释只是为了忽略其他 JerseyLink特性。
@JsonIgnoreProperties({ "uriBuilder", "params", "type", "rels" })

有没有人使用过与 Jersey 的声明式链接并具有预期的 JSON 输出(例如, href 而不是 uri ,没有额外的 Jersey 属性)而不必使用 JsonIgnoreProperties或其他黑客?

谢谢。

编辑

我使用一种我认为是 hack 但对我来说效果很好的方法解决了这个问题,并且不需要使用复杂的适配器。

我意识到我实际上可以公开一个不同的对象,而不是由 Jersey 注入(inject)的 Link。

我创建了一个名为 ResourceLink 的包装对象:
public class ResourceLink {
private String rel;
private URI href;

//getters and setters
}

然后在我的表示对象中,我有一个 getter 方法:
public ResourceLink getLink() {
ResourceLink link = new ResourceLink();
link.setRel(self.getRel());
link.setHref(self.getUri());

return link;
}

所以我使用 Jersey 来注入(inject)链接,但在我的表示对象中的 getter 方法中返回了一个不同的对象。这将是序列化为 JSON 的属性,而不是注入(inject)的链接对象,因为我没有为它创建 getter 方法。

最佳答案

调查

环境: 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);
}

不同提供商的结果(没有额外的配置)

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) 注释该字段.让我们看一下这个适配器的片段
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-providerJacksonJsonProvider 2) , 创建 ContextResolverObjectMapper我们在哪里 不要注册 JaxbAnnotationModule .你似乎正在做其中之一。

解决方案

以上仍然没有让我们到达我们想要到达的地方(即没有 params)。

对于 jersey-media-json-jacksonjackson-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();
}
}

然后创建一个 ContextResolverObjectMapper ,我们在这里注册序列化器
@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 ,似乎有一个 BugJaxbLink 中缺少 setter类,所以编码恢复到调用 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 版本。这实际上可能是更优选的解决方案。无需创建 ContextResolversObjectMapper :-D

也可以在包级别声明注释,如 here 所示。

关于java - JAX-RS HATEOAS 使用 Jersey,JSON 中不需要的链接属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24968448/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com