如何在 Jersey 2.x 中返回对象
Posted
技术标签:
【中文标题】如何在 Jersey 2.x 中返回对象【英文标题】:How to return objects in Jersey 2.x 【发布时间】:2016-02-29 14:53:42 【问题描述】:我有一个管理 Parada 对象的网络服务。我想要实现的目标似乎很简单:返回这些对象的列表:
List<Parada> list
这个列表是使用一个使用另一个 DAO 类的 Service 类返回的,只是将其注释掉。
此外,我的常见做法是每个 Web 方法都使用 ResponseBuilder 返回一个响应,如下所示:
return Response.ok(obj, MediaType.APPLICATION_JSON).build();
这是我的一种网络方法的示例:
@GET
@Consumes(value = MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
@Path("idParadaGtfs")
public Response getParadasPorIdGtfs(
@PathParam(value = "idParadaGtfs") Integer pCodigoParadaEnGtfs
)
try
getServiceIfNecessary();
List<Parada> paradas = service.getParadas(pCodigoParadaEnGtfs);
return Response.ok(paradas, MediaType.APPLICATION_JSON).build();
catch(HibernateException e)
String msg = "Error HibernateException: " + e.getMessage();
LogHelper.logError(logger, msg, true);
e.printStackTrace();
return Response.serverError().tag(msg).build();
catch(Exception e)
String msg = "Error Exception: " + e.getMessage();
LogHelper.logError(logger, msg, true);
e.printStackTrace();
return Response.serverError().tag(msg).build();
很遗憾,我没有收到任何对象,并且每次执行上述 Web 方法时都会收到以下错误:
nov 26, 2015 2:20:16 PM org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
GRAVE: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List<model.Parada>.
我必须实现什么才能让我的网络方法使用列表构建响应?
谢谢!
编辑:
我已经能够通过进行一些更改和添加来使其工作,我现在将对其进行描述。
首先,我添加了一个 Parada 容器类,ParadaContainer:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import com.ingartek.ws.paradasasociadasws.model.Parada;
@XmlRootElement
public class ParadaContainer implements Serializable
private static final long serialVersionUID = 6535386309072039406L;
private List<Parada> paradas;
public ParadaContainer(ArrayList<Parada> pParadas)
this.setParadas(pParadas);
public List<Parada> getParadas()
return paradas;
public void setParadas(List<Parada> paradas)
this.paradas = paradas;
@Override
public String toString()
StringBuilder builder = new StringBuilder();
builder.append("ParadaContainer [");
if (paradas != null)
builder.append("paradas=");
for(Parada p : paradas)
builder.append(p.toString());
builder.append("]");
return builder.toString();
现在,我不返回 Parada 对象列表,而是返回单个 ParadaContainer 对象:
ParadaContainer paradas = new ParadaContainer(new ArrayList<Parada>(service.getParadas()));
return Response
.ok(paradas)
.type(MediaType.APPLICATION_JSON)
.build();
我不知道它们是否是强制性的,但我已经创建了另一个类 (MyObjectMapperProvider)...
import javax.ws.rs.ext.ContextResolver;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>
final ObjectMapper defaultObjectMapper;
public MyObjectMapperProvider()
defaultObjectMapper = createDefaultMapper();
@Override
public ObjectMapper getContext(Class<?> type)
return defaultObjectMapper;
private static ObjectMapper createDefaultMapper()
final ObjectMapper result = new ObjectMapper();
result.configure(SerializationFeature.INDENT_OUTPUT, true);
return result;
...并编辑了我的 Application 类并添加了一些行(请参阅 *Jackson * 评论,直到 Classes de Servicios 评论):
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
import org.glassfish.jersey.jackson.JacksonFeature;
import com.ingartek.ws.paradasasociadasws.ws.ParadasWS;
public class App extends Application
private final Set<Class<?>> classes;
public App()
HashSet<Class<?>> c = new HashSet<Class<?>>();
// Filtro CORS:
c.add(CORSFilter.class);
// Jackson
c.add(MyObjectMapperProvider.class);
c.add(JacksonFeature.class);
// Clases de Servicios:
c.add(ParadasWS.class);
classes = Collections.unmodifiableSet(c);
@Override
public Set<Class<?>> getClasses()
return classes;
之后,我通过向它们添加一些注释来编辑我的类模型(@XmlRootElement 和 @JsonProperty;删除了不相关的 getter、setter、hashCode、equals 和 toString 方法):
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
import com.fasterxml.jackson.annotation.JsonProperty;
@XmlRootElement(name = "grupo")
@Entity
@Table(name = "grupos_cercania_exacta")
public class Grupo implements Serializable
@Transient
private static final long serialVersionUID = -5679016396196675191L;
@JsonProperty("id")
@Id
@Column(name = "id_grupo")
private Integer id;
...
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
import com.fasterxml.jackson.annotation.JsonProperty;
@XmlRootElement(name = "operador")
@Entity
@Table(name = "operadores_asociados")
public class Operador implements Serializable
@Transient
private static final long serialVersionUID = -7557099187432476588L;
/*
Atributos
*/
@JsonProperty("codigo")
@Id
@Column(name = "codigo_operador", insertable = false, updatable = false)
private Integer codigo;
@JsonProperty("nombre")
@Column(name = "descripcion_corta", insertable = false, updatable = false)
private String nombre;
@JsonProperty("descripcion")
@Column(name = "descripcion_larga", insertable = false, updatable = false)
private String descripcion;
@JsonProperty("web")
@Column(name = "direccion_web", insertable = false, updatable = false)
private String web;
@JsonProperty("telefono")
@Column(name = "telefono", insertable = false, updatable = false)
private String telefono;
...
import java.io.Serializable;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlRootElement;
import com.fasterxml.jackson.annotation.JsonProperty;
@XmlRootElement(name = "parada")
@Entity
@Table(name = "paradas_asociadas")
public class Parada implements Serializable
@Transient
private static final long serialVersionUID = -3594254497389126197L;
@JsonProperty("id")
@Id
@Column(name = "id")
private UUID id;
@JsonProperty("codigoMunicipio")
@Column(name = "codigo_municipio")
private Integer codigoMunicipio;
@JsonProperty("nombre")
@Column(name = "nombre")
private String nombre;
@JsonProperty("descripcion")
@Column(name = "descripcion")
private String descripcion;
@JsonProperty("idGtfs")
@Column(name = "id_gtfs")
private Integer idGtfs;
@JsonProperty("idWs")
@Column(name = "id_ws")
private Integer idWs;
@JsonProperty("latitud")
@Column(name = "latitud")
private Double latitud;
@JsonProperty("longitud")
@Column(name = "longitud")
private Double longitud;
@JsonProperty("utmX")
@Column(name = "utm_x")
private Double utmX;
@JsonProperty("utmY")
@Column(name = "utm_y")
private Double utmY;
@JsonProperty("grupo")
@ManyToOne
@JoinColumn(name = "grupo_cercania_exacta_id")
private Grupo grupo;
@JsonProperty("operador")
@ManyToOne
@JoinColumn(name = "operador")
private Operador operador;
...
我不得不承认,在进行这些更改之后,我遇到了一些问题。敏锐的人可能已经意识到之前的 Parada 类缺少一个属性:缺少 Point 属性。这个属性给我带来了一些问题,即没有序列化器和序列化器阻止了我创建成功的 JSON。于是我google了一下,发现了三个选项:
-
删除点项目。这是我的最终选择,因为 Point 是多余的,因为存在纬度和经度元素,而且它只会打扰或混淆最终用户。
创建自定义序列化器和反序列化器。幸运的是我找到了以下link,它描述了创建它们的过程。以下在here 中有描述:
将这些注释添加到我们的坐标字段中:
@JsonSerialize(using = PointToJsonSerializer.class)
@JsonDeserialize(using = JsonToPointDeserializer.class)
创建这样的序列化程序:
import java.io.IOException;
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 com.vividsolutions.jts.geom.Point;
public class PointToJsonSerializer extends JsonSerializer<Point>
@Override
public void serialize(Point value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException
String jsonValue = "null";
try
if(value != null)
double lat = value.getY();
double lon = value.getX();
jsonValue = String.format("POINT (%s %s)", lat, lon);
catch(Exception e)
jgen.writeString(jsonValue);
创建这样的反序列化器:
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.PrecisionModel;
public class JsonToPointDeserializer extends JsonDeserializer<Point>
private final static GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 26910);
@Override
public Point deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
try
String text = jp.getText();
if(text == null || text.length() <= 0)
return null;
String[] coordinates = text.replaceFirst("POINT ?\\(", "").replaceFirst("\\)", "").split(" ");
double lat = Double.parseDouble(coordinates[0]);
double lon = Double.parseDouble(coordinates[1]);
Point point = geometryFactory.createPoint(new Coordinate(lat, lon));
return point;
catch(Exception e)
return null;
-
最后一个选项是使用 Jackson Datatype JTS 库,其 github 存储库位于 here。
我花了几个小时才找到这些解决方案,但最终我得到了它们。希望它对某人有所帮助。谢谢!
【问题讨论】:
【参考方案1】:不允许发回列表。可能是因为 List
没有 @XmlRootElement
符号。您可以创建自己的容器:
@XmlRootElement
public class ParadaContainer implements Serializable
private List<Parada> list;
public List<Parada> getList()
return list;
public void setList(List<Parada> list)
this.list = list;
你的部分看起来像:
try
getServiceIfNecessary();
List<Parada> paradas = service.getParadas(pCodigoParadaEnGtfs);
ParadaContainer paradaContainer = new ParadaContainer();
paradaContainer.setList(paradas);
return Response.ok(paradaContainer, MediaType.APPLICATION_JSON).build();
【讨论】:
感谢您的帮助,但它不起作用...这是我得到的错误:2015 年 11 月 26 日下午 4:05:18 org.glassfish.jersey.message.internal .WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo GRAVE:找不到媒体类型 = 应用程序/json,类型 = com.ingartek.ws.paradasasociadasws.model.extend.ParadaContainer 类的 MessageBodyWriter,genericType = com.ingartek.ws.paradasasociadasws.model.extend 类。 ParadaContainer。【参考方案2】:要么你没有 JSON 提供程序(我猜你有),要么你正在使用 MOXy。在后一种假设下,对于 MOXy,它需要知道类型信息才能进行序列化。当您返回 Response
时,您正在包装对象,这会带走类型信息(因为类型擦除),而不是如果您正在这样做
@GET
public List<Parada> get()
这里的类型信息是已知的。但是在做
@GET
public Response get()
List<Parada> list..
return Response.ok(list)...
在实体到达处理的序列化阶段时,该类型被隐藏和擦除。
为了解决这个问题,引入了GenericEntity
通常类型擦除会删除通用类型信息,这样一个包含例如
List<String>
类型实体的响应实例在运行时似乎包含一个原始List<?>
。当需要泛型类型选择合适的MessageBodyWriter
时,可以使用该类来包装实体并捕获其泛型类型。
所以你可以这样做
List<Parada> paradas = ...
GenericEntity<List<Parada>> entity = new GenericEntity<List<Parada>>(paradas);
return Response.ok(entity, ...)...
第二个选项,不是使用 MOXy,而是使用 Jackson。使用 Jackson,不需要类型信息(在大多数情况下),因为序列化程序只是内省和 bean bean 属性来获取数据。
【讨论】:
这个链接值得阅读吗? jersey.java.net/documentation/latest/media.html#json.jackson 如果你想使用 Jackson 作为你的 JSON 提供者而不是 MOXy(假设你正在使用它) 根据您在另一个答案中的评论,您似乎甚至没有 JSON 提供程序。在发布此问题之前,您是否能够让 任何 JSON 工作? 不,我不是,但我已经能够使用 Jackson Parser。我正在编辑我的答案。以上是关于如何在 Jersey 2.x 中返回对象的主要内容,如果未能解决你的问题,请参考以下文章
如何在Java Jersey REST服务中强制使用queryparams?
如何在 Spring MVC 中使用 Jackson 和 Jersey 2 Client 反序列化 Joda DateTime?
如何在球衣中使用 swagger 和 ResourceConfig?