web-dev-qa-db-fra.com

Jersey: Retourne une liste de chaînes

J'essaie de renvoyer une liste de chaînes dans Jersey sous forme de code JSON et XML. Je pensais que ce serait trivial.

Mon premier essai a été d'écrire quelque chose comme ça

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/bar")
public List<String> get() {
    return dao.get();
}

et je m'attendais à une sortie similaire à celle-ci: ["string1", ..., "stringN] malheureusement, j'ai ceci:

com.Sun.jersey.api.MessageException: A message body writer for Java class Java.util.LinkedList, and Java type Java.util.List<Java.lang.String>, and MIME media type application/json was not found 

Puis j'ai écrit un wrapper StringList for List

@XmlRootElement
public class StringList {

    private List<String> data;

    public StringList() {
    }

    public StringList(List<String> data) {
        this.data = data;
    }

    public List<String> getData() {
        return data;
    }

    public void setData(List<String> data) {
        this.data = data;
    }
}

et modifié la façade pour

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/foo")
public StringList stringlist() {
    return new StringList(sl());
}

Ce qui fonctionne très bien pour les listes avec plus d'éléments que 1.

{"data":["foo","bar"]}

Malheureusement, j'obtiens deux résultats inattendus pour un ou zéro élément

{"data": "just one"} // for one element i would expect {"data": ["just one"]}

null // for no elements i would expect {"data": []}

Est-ce que je fais quelque chose de complètement faux? Comment puis-je résoudre ce problème?

17
user524824

Vous pouvez utiliser javax.ws.rs.core.GenericEntity:

@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Path("/foo")
public GenericEntity<List<String>> stringlist()
{
  List<String> list = Arrays.asList("test", "as");

  return new GenericEntity<List<String>>(list) {};
}
6
sdorra

D'accord, je pourrais le réparer en cherchant dans les échantillons

Cela fonctionne, mais il ne peut être utilisé que pour JSON et non pour XML.

@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/get")
public JSONArray get() {;
    return new JSONArray(dao.getStringList());
}

Résout le problème, mais existe-t-il également une approche générique?

8
user524824

Afin de convaincre Jersey de produire les listes à votre guise, vous devez fournir votre propre ContextResolver:

@Provider
public class JaxbContentResolver implements ContextResolver<JAXBContext> {

    private Log log = LogFactory.getLog(getClass());
    private JAXBContext context;

    public JaxbContentResolver() throws JAXBException {
        Class[] types = {StringList.class};
        context = new JSONJAXBContext(JSONConfiguration.mapped().rootUnwrapping(true).arrays("data").build(), types);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        log.trace("Entering Test-getContext for type: " + objectType.getSimpleName());
        return context;
    }
}

De cette façon, les listes apparaîtront comme vous le souhaitez.

REMARQUE: l'un des inconvénients de cette approche est que vous devez conserver une place supplémentaire dans votre code. Si vous souhaitez ajouter une autre classe (wrapper liste) à votre interface REST, vous devez vous rappeler d’aller au code ci-dessus et d’ajouter cette classe dans votre ContextResolver ...

1
Julius Blank

En plus des réponses fournies, si vous obtenez toujours MessageBodyWriter not found, essayez d'ajouter une dépendance telle que:

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-moxy</artifactId>
    </dependency>
0
Skylar Saveland

Ma solution à cela était une classe d'emballage (trouvée quelque part). Ça fonctionne maintenant. Je ne comprends pas ce qui se cache derrière le fait de ne pas soutenir la classe javas List en tant qu'élément racine. Peut-être cela a-t-il à voir avec certaines spécifications/meilleures pratiques JSON dont je ne suis pas au courant.

Mais pour l'instant, j'utilise ceci:

public class Houses {
    private List<String> houses;

    // Needed for jersey
    public Houses() { }

    public Houses(List<String> houses) {
        this.houses = houses;
    }

    public void setHouses(List<String> houses) {
        this.houses = houses;
    }

    public List<String> getHouses() {
        return this.houses;
    }
}
0
sigi

Vous devez utiliser la configuration alternative JSON JSONConfiguration.natural().

Il est préférable de créer votre propre ContextResolver en utilisant cette configuration alternative en tant que fournisseur et en lui indiquant les classes dont il est responsable.

Je ne suis pas au courant d'une méthode pour utiliser la configuration alternative globalement d'une autre manière.

0
someone