108 votes

Renvoyer un objet JSON comme réponse dans Spring Boot

J'ai un exemple de RestController dans Spring Boot :

@RestController
@RequestMapping("/api")
class MyRestController
{
    @GetMapping(path = "/hello")
    public JSONObject sayHello()
    {
        return new JSONObject("{'aa':'bb'}");
    }
}

J'utilise la bibliothèque JSON org.json

Quand j'ai touché l'API /hello j'obtiens une exception disant :

Servlet.service() pour la servlet [dispatcherServlet] dans le contexte avec le chemin d'accès [] a généré une exception [Le traitement de la demande a échoué ; l'exception imbriquée est la suivante java.lang.IllegalArgumentException : Aucun convertisseur trouvé pour le retour de type : class org.json.JSONObject] avec comme cause première

java.lang.IllegalArgumentException : Aucun convertisseur trouvé pour le retour de type : class org.json.JSONObject

Quel est le problème ? Quelqu'un peut-il expliquer ce qui se passe exactement ?

0 votes

Jackson ne peut pas convertir JSONObject en json.

0 votes

Ok, je comprends ça. Que peut-on faire pour réparer cela ?

1 votes

Je veux que la réponse soit construite à la volée. Je ne veux pas créer de classes spécifiques pour chaque réponse.

126voto

prem kumar Points 2649

Comme vous utilisez Spring Boot web, la dépendance de Jackson est implicite et nous n'avons pas à la définir explicitement. Vous pouvez vérifier la dépendance de Jackson dans votre pom.xml dans l'onglet de la hiérarchie des dépendances si vous utilisez eclipse.

Et comme vous l'avez annoté avec @RestController il n'est pas nécessaire d'effectuer une conversion json explicite. Il suffit de retourner un POJO et le sérialiseur jackson se chargera de la conversion en json. C'est équivalent à l'utilisation de @ResponseBody lorsqu'il est utilisé avec @Controller. Plutôt que de placer @ResponseBody sur chaque méthode de contrôleur, nous plaçons @RestController au lieu de la vanille @Controller y @ResponseBody est appliqué par défaut à toutes les ressources de ce contrôleur.
Consultez ce lien : https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

Le problème que vous rencontrez est que l'objet retourné (JSONObject) n'a pas de getter pour certaines propriétés. Et votre intention n'est pas de sérialiser ce JSONObject mais plutôt de sérialiser un POJO. Il suffit donc de renvoyer le POJO.
Consultez ce lien : https://stackoverflow.com/a/35822500/5039001

Si vous voulez retourner une chaîne sérialisée json, il suffit de retourner la chaîne. Spring utilisera StringHttpMessageConverter au lieu du convertisseur JSON dans ce cas.

0 votes

Si vous voulez retourner une chaîne json à partir de java, vous pouvez simplement retourner une chaîne si elle est déjà sérialisée en json. Il n'est pas nécessaire de convertir la chaîne en json et le json en chaîne.

9 votes

Si vous souhaitez renvoyer un ensemble de paires nom-valeur sans structure rigide au moment de la compilation, vous pouvez renvoyer un fichier Map<String,Object> ou un Properties objet

0 votes

@prem kumar question aléatoire : que voulez-vous dire par "au lieu de vanilla Controller et ResponseBody" ? Qu'est-ce qui est vanille ici ?

87voto

g00glen00b Points 10383

La raison pour laquelle votre approche actuelle ne fonctionne pas est que Jackson est utilisé par défaut pour sérialiser et désérialiser les objets. Cependant, il ne sait pas comment sérialiser l'objet JSONObject . Si vous souhaitez créer une structure JSON dynamique, vous pouvez utiliser un fichier de type Map par exemple :

@GetMapping
public Map<String, String> sayHello() {
    HashMap<String, String> map = new HashMap<>();
    map.put("key", "value");
    map.put("foo", "bar");
    map.put("aa", "bb");
    return map;
}

Cela donnera lieu à la réponse JSON suivante :

{ "key": "value", "foo": "bar", "aa": "bb" }

C'est un peu limité, car il peut devenir un peu plus difficile d'ajouter des objets enfants. Jackson dispose cependant de son propre mécanisme, utilisant ObjectNode y ArrayNode . Pour l'utiliser, vous devez faire un câblage automatique. ObjectMapper dans votre service/contrôleur. Vous pouvez alors utiliser :

@GetMapping
public ObjectNode sayHello() {
    ObjectNode objectNode = mapper.createObjectNode();
    objectNode.put("key", "value");
    objectNode.put("foo", "bar");
    objectNode.put("number", 42);
    return objectNode;
}

Cette approche vous permet d'ajouter des objets enfants, des tableaux et d'utiliser tous les différents types.

3 votes

Qu'est-ce que le mapper ici ?

1 votes

@iwekesi c'est le Jackson ObjectMapper vous devriez faire un autowire (voir le paragraphe au-dessus de mon dernier extrait de code).

2 votes

Il est éblouissant de savoir qu'il faut aller si loin pour produire des objets JSON significatifs ! Il est également triste que Pivotal fasse pas de effort du tout ( spring.io/guides/gs/actuator-service ) pour signaler ces limitations. Heureusement, nous avons SO ! ;)

50voto

Sangam Belose Points 2072

Vous pouvez soit renvoyer une réponse en tant que String comme suggéré par @vagaasen ou vous pouvez utiliser ResponseEntity Objet fourni par Spring comme ci-dessous. De cette façon, vous pouvez également retourner Http status code ce qui est plus utile pour les appels de services Web.

@RestController
@RequestMapping("/api")
public class MyRestController
{

    @GetMapping(path = "/hello", produces=MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Object> sayHello()
    {
         //Get data from service layer into entityList.

        List<JSONObject> entities = new ArrayList<JSONObject>();
        for (Entity n : entityList) {
            JSONObject entity = new JSONObject();
            entity.put("aa", "bb");
            entities.add(entity);
        }
        return new ResponseEntity<Object>(entities, HttpStatus.OK);
    }
}

2 votes

Si j'ajoute JSONObject dans les entités, je reçois à nouveau une exception similaire.

0 votes

@Sangam votre réponse m'a aidé pour un autre problème lié à jackson-dataformat-xml

0 votes

Cela a été d'une grande aide ! Merci !

12voto

mahi Points 205

Vous pouvez également utiliser un hashmap pour cela

@GetMapping
public HashMap<String, Object> get() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("key1", "value1");
    map.put("results", somePOJO);
    return map;
}

6voto

Dmitry Points 114
@RequestMapping("/api/status")
public Map doSomething()
{
    return Collections.singletonMap("status", myService.doSomething());
}

PS. Ne fonctionne que pour 1 valeur

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X