繁体   English   中英

如何在Jersey 2.x中返回对象

[英]How to return objects in Jersey 2.x

我有一个管理Parada对象的Web服务。 我想要实现的目标似乎很简单:返回这些对象的列表:

List<Parada> list

使用Service类返回此列表,该Service类使用另一个DAO类,只是将其注释掉。

此外,我的常规做法是每个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>.

我必须实现什么才能使我的Web方法使用列表构建响应?

谢谢!

编辑

我已经能够通过进行一些更改和添加来使其工作,下面将对其进行描述。

首先,我添加了一个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属性。 这个属性给我带来了一些问题,这是因为缺少序列化程序,而序列化程序使我无法创建成功的JSON。 因此,我用Google搜索出来,发现了三个选择:

  1. 删除点项目。 这是我的最终选择,因为由于存在纬度和经度元素,并且因为它会打扰或混淆最终用户,所以Point是多余的。
  2. 创建一个自定义的序列化器和反序列化器。 幸运的是,我找到了以下链接 ,该链接描述了创建它们的过程。 描述以下内容:

将这些注释添加到我们的坐标字段中:

@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;
        }
    }

}
  1. 最后一个选择是使用Jackson数据类型JTS库,其github存储库位于此处

我花了几个小时才能找到这些解决方案,但最终我得到了它们。 希望它对某人有帮助。 谢谢!

您没有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的属性来获取数据。

不允许发送回列表。 可能是因为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();
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM