arj*_*ion 21 java rest jax-rs jersey jersey-2.0
从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;
Run Code Online (Sandbox Code Playgroud)
......理论上预计会产生这样的JSON:
"link" : {
"rel" : "self",
"href" : "http://localhost/api/resource/1"
}
Run Code Online (Sandbox Code Playgroud)
但是,Jersey会生成不同的JSON,其中包含许多我不需要的属性:
"link" : {
"rel" : "self",
"uri" : "http://localhost/api/resource/1",
"type": null,
"uriBuilder" : null
}
Run Code Online (Sandbox Code Playgroud)
另请注意href,它使用的是代替uri.我查看了Jersey Link对象的实现并找到了JerseyLink.
我想使用Jersey的声明性链接,而不是推出我自己的实现.我最终使用Jackson注释只是为了忽略其他JerseyLink属性.
@JsonIgnoreProperties({ "uriBuilder", "params", "type", "rels" })
Run Code Online (Sandbox Code Playgroud)
有没有人使用与Jersey的声明性链接并且具有预期的JSON输出(例如,href而不是uri没有额外的Jersey属性)而不必使用JsonIgnoreProperties或其他黑客?
谢谢.
编辑
我使用一种方法解决了这个问题,我认为这是一种黑客攻击,但对我来说效果很好,并且不需要使用复杂的适配器.
我意识到我实际上可以暴露一个不同的对象,而不是泽西注入的链接.
我创建了一个名为ResourceLink的包装器对象:
public class ResourceLink {
private String rel;
private URI href;
//getters and setters
}
Run Code Online (Sandbox Code Playgroud)
然后在我的表示对象中,我有一个getter方法:
public ResourceLink getLink() {
ResourceLink link = new ResourceLink();
link.setRel(self.getRel());
link.setHref(self.getUri());
return link;
}
Run Code Online (Sandbox Code Playgroud)
所以我使用Jersey来注入链接,但在我的表示对象中的getter方法中返回了一个不同的对象.这将是序列化为JSON而不是注入的链接对象的属性,因为我没有为它创建getter方法.
Pau*_*tha 20
环境:泽西岛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);
}
Run Code Online (Sandbox Code Playgroud)
新泽西媒体MOXY
依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)
结果(怪异)
{
"link": "javax.ws.rs.core.Link$JaxbLink@cce17d1b"
}
Run Code Online (Sandbox Code Playgroud)
新泽西媒体JSON - 杰克逊
依赖
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)
结果(关闭,但是有什么params?)
{
"link": {
"params": {
"rel": "stackoverflow"
},
"href": "https://stackoverflow.com/questions/24968448"
}
}
Run Code Online (Sandbox Code Playgroud)
杰克逊JAXRS JSON的提供商
依赖
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
结果(两个不同的结果,有两个不同的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"
}
}
Run Code Online (Sandbox Code Playgroud)
resourceConfig.register(JacksonJaxbJsonProvider.class);
{
"link": {
"params": {
"rel": "stackoverflow"
},
"href": "https://stackoverflow.com/questions/24968448"
}
}
Run Code Online (Sandbox Code Playgroud)
我们正在诠释这个领域@XmlJavaTypeAdapter(Link.JaxbAdapter.class).让我们看一下这个适配器的片段
public static class JaxbAdapter extends XmlAdapter<JaxbLink, Link> {...}
Run Code Online (Sandbox Code Playgroud)
所以Link,我们正在被组织起来JaxbLink
public static class JaxbLink {
private URI uri;
private Map<QName, Object> params;
...
}
Run Code Online (Sandbox Code Playgroud)
新泽西媒体MOXY
似乎是一个错误...请参阅下面的解决方案.
其他
另外两个依赖于jackson-module-jaxb-annotations使用JAXB注释处理编组.jersey-media-json-jackson将自动注册所需JaxbAnnotationModule.因为jackson-jaxrs-json-provider,using JacksonJsonProvider不支持JAXB注释(没有混淆),而using JacksonJsonJaxbProvider将为我们提供JAXB注释支持.
因此,如果我们有 JAXB注释支持,我们将编组到JaxbLink,这将得到这个结果
{
"link": {
"params": {
"rel": "stackoverflow"
},
"href": "https://stackoverflow.com/questions/24968448"
}
}
Run Code Online (Sandbox Code Playgroud)
该方法,我们可以得到所有不需要的属性的结果,是1) ,使用jackson-jaxrs-json-provider的JacksonJsonProvider或2) ,创造ContextResolver了ObjectMapper我们不注册JaxbAnnotationModule.你好像在做其中的一件事.
以上仍然没有让我们到达我们想去的地方(即没有params).
对于jersey-media-json-jackson和jackson-jaxrs-json-provider...
...使用杰克逊,我现在唯一能想到的就是创建一个自定义序列化器
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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后创建一个ContextResolverfor ObjectMapper,我们注册序列化器
@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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是结果
{
"link": {
"rel": "stackoverflow",
"href": "https://stackoverflow.com/questions/24968448"
}
}
Run Code Online (Sandbox Code Playgroud)
使用jersey-media-moxy,似乎有一个Bug在JaxbLink类中缺少setter ,所以编组恢复到调用toString,这就是上面所示.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;
}
}
Run Code Online (Sandbox Code Playgroud)
改为使用此适配器
@XmlElement(name = "link")
@XmlJavaTypeAdapter(LinkAdapter.class)
private Link link;
Run Code Online (Sandbox Code Playgroud)
会给我们所需的输出
{
"link": {
"href": "https://stackoverflow.com/questions/24968448",
"rel": "stackoverflow"
}
}
Run Code Online (Sandbox Code Playgroud)
现在我考虑一下,这LinkAdapter也将与杰克逊提供商合作.无需创建Jackson Serializer/Deserializer.鉴于JacksonFeature已启用,Jackson模块应已经支持JAXB注释.上面的示例显示了单独使用JAXB/JSON提供程序,但只要JacksonFeature启用了,就应该使用提供程序的JAXB版本.这实际上可能是更优选的解决方案.无需ContextResolvers为ObjectMapper:-D 创建一个
它也可以在包级别声明注释,如看到这里
| 归档时间: |
|
| 查看次数: |
12529 次 |
| 最近记录: |