我有一个管理 Parada 对象的网络服务。我想要实现的目标似乎很简单:返回这些对象的列表:
List<Parada> list
使用另一个DAO类的Service类返回此列表,只是将其注释掉。
此外,我的常见做法是每个Web方法都使用ResponseBuilder返回一个Response,如下所示:
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 *评论直到 Clases 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属性。这个属性给我带来了一些问题,也就是说,没有Serializer和Serializer阻止我创建一个成功的JSON。所以我用Google搜索出来并找到了三个选项:
将这些注释添加到我们的坐标字段:
@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;
}
}
}
我持续了几个小时才能找到这些解决方案,但最终我得到了它们。希望对某人有帮助。谢谢!
答案 0 :(得分:3)
要么你没有JSON提供者(我猜你这么做),要么你正在使用MOXy。在后一种假设下,使用MOXy,它需要知道类型信息才能进行序列化。当你返回Response
时,你正在包装对象,它会删除类型信息(因为类型擦除),而不是你正在做的事情
@GET
public List<Parada> get() {}
此处类型信息是已知的。但是做了
@GET
public Response get() {
List<Parada> list..
return Response.ok(list)...
}
当实体到达处理的序列化阶段时,类型被隐藏和擦除。
为了解决这个问题,我引入了GenericEntity
通常,类型擦除会删除泛型类型信息,以便包含例如类型
List<String>
的实体的Response实例在运行时似乎包含原始List<?>
。当需要泛型类型来选择合适的MessageBodyWriter
时,此类可用于包装实体并捕获其泛型类型。
所以你可以做到
List<Parada> paradas = ...
GenericEntity<List<Parada>> entity = new GenericEntity<List<Parada>>(paradas){};
return Response.ok(entity, ...)...
第二个选项是,而不是使用MOXy,而是使用Jackson。使用Jackson时,不需要类型信息(在大多数情况下),因为序列化程序只是内省并且bean bean属性可以获取数据。
答案 1 :(得分:0)
不允许发回List。可能是因为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();
}