【发布时间】: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。
我花了几个小时才找到这些解决方案,但最终我得到了它们。希望它对某人有所帮助。谢谢!
【问题讨论】:
标签: java jax-rs jersey-2.0