SpringBootで応答としてJSONオブジェクトを返す


88

SpringBootにサンプルのRestControllerがあります。

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

JSONライブラリを使用しています org.json

APIを押すと/hello、次のような例外が発生します。

パス[]のコンテキストでサーブレット[dispatcherServlet]のServlet.service()が例外をスローしました[リクエスト処理に失敗しました。ネストされた例外はjava.lang.IllegalArgumentExceptionです:タイプの戻り値のコンバーターが見つかりません:クラスorg.json.JSONObject]根本的な原因

java.lang.IllegalArgumentException:タイプの戻り値のコンバーターが見つかりません:クラスorg.json.JSONObject

どうした?誰かが正確に何が起こっているのか説明できますか?


JacksonはJSONObjectをjsonに変換できません。
ポー2017年

わかりました。これを修正するために何ができますか?
iwekesi 2017年

1
応答をその場で構築したい。応答ごとに特定のクラスを作成したくありません。
iwekesi 2017年

2
メソッドをStringとして返す方がよい場合があります。さらに、アノテーション@ResponseBodyをメソッドに追加することもできます。これにより、要求に応じて応答が処理されます:-)@GetMapping(path = "/hello") @ResponseBody public String sayHello() {return"{'aa':'bb'}";}
vegaasen 2017年

@vegaasen ResponseBodyについて少し詳しく説明していただけますか
iwekesi 2017年

回答:


109

Spring Boot Webを使用しているため、Jacksonの依存関係は暗黙的であり、明示的に定義する必要はありません。pom.xmlEclipseを使用している場合は、[依存関係階層]タブでJacksonの依存関係を確認できます。

そして、あなたが注釈を付け@RestControllerたように、明示的なjson変換を行う必要はありません。POJOを返すだけで、jacksonシリアライザーがjsonへの変換を処理します。これは、使用するのと同じです@ResponseBody@Controllerで使用した場合。むしろ置くよりも@ResponseBody、すべてのコントローラメソッドの我々の場所に@RestController代わりにバニラの@Controller@ResponseBody、デフォルトでは、そのコントローラ内のすべてのリソースに適用されます。
このリンクを参照してください: https //docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

直面している問題は、返されたオブジェクト(JSONObject)に特定のプロパティのゲッターがないためです。そして、あなたの意図は、このJSONObjectをシリアル化することではなく、POJOをシリアル化することです。したがって、POJOを返すだけです。
このリンクを参照してください: https //stackoverflow.com/a/35822500/5039001

jsonのシリアル化された文字列を返したい場合は、文字列を返すだけです。この場合、SpringはJSONコンバーターの代わりにStringHttpMessageConverterを使用します。


json文字列がJavaから返したいものである場合、すでにjsonがシリアル化されていれば、文字列を返すことができます。文字列をjsonに変換し、jsonを文字列に戻す必要はありません。
prem kumar 2017年

6
厳密なコンパイル時構造なしで名前と値のペアのセットを返したい場合は、Map<String,Object>またはPropertiesオブジェクトを返すことができます
Vihung 2017

@prem kumarランダムな質問:「バニラコントローラーとResponseBodyの代わりに」とはどういう意味ですか?ここのバニラは何ですか?
OrkunOzen18年

私は通常のコントローラーを意味し、すべてのリクエストメソッドにResponseBodyアノテーションが配置されています。
prem kumar 2018年

68

現在のアプローチが機能しない理由は、Jacksonがデフォルトでオブジェクトのシリアル化と逆シリアル化に使用されるためです。ただし、をシリアル化する方法がわかりませんJSONObject。動的なJSON構造を作成する場合は、次のように使用できますMap

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

これにより、次のJSON応答が発生します。

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

子オブジェクトを追加するのが少し難しくなる可能性があるため、これは少し制限されています。ジャクソンには独自のメカニズムがありますが、とを使用ObjectNodeしていArrayNodeます。これを使用するにObjectMapperは、サービス/コントローラーを自動配線する必要があります。次に、以下を使用できます。

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

このアプローチにより、子オブジェクト、配列を追加し、さまざまなタイプをすべて使用できます。


2
ここのマッパーとは何ですか?
iwekesi 2017年

1
@iwekesiは、自動配線ObjectMapperする必要があるジャクソンです(私の最後のコードスニペットの上の段落を参照してください)。
g00glen00b 2017年

1
意味のあるJSONオブジェクトを生成するには、このような長さを費やす必要があることを知っているのは驚くべきことです。Pivotalがノーを作るのも悲しいですこれらの制限を呼び出すためにまったく努力し(spring.io/guides/gs/actuator-service)。幸いなことに、私たちはSOを持っています!;)
cogitoergosum19年

@HikaruShindojava.util.HashMapは、Java1.2以降のJavaのコア機能です。
g00glen00b

44

String@vagaasenによって提案されたように応答を返すかResponseEntity、以下のようにSpringによって提供されたオブジェクトを使用することができます。このようにしてHttp status code、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);
    }
}

1
エンティティにJSONObjectを追加すると、同様の例外が発生します
iwekesi 2017年

@Sangamあなたの答えは、jackson-dataformat-xmlに関連する別の問題について私を助けてくれました
神聖な

これは大きな助けになりました!ありがとうございました!
ジョーンズクリス

1
なぜこの答えはもっと賛成されないのだろうか。私もSpringを初めて使用するので、これが優れたソフトウェアエンジニアリング手法であるかどうかはわかりません。そうは言っても、この答えは本当に役に立ちました。しかし、私はで多くの問題を抱えていましたJSONObjectが、SpringはJacksonを使用しているので、HashMap代わりにそれを変更し、この回答で読んだコードは機能しました。
Melvin Roest 2018

2
MediaType.APPLICATION_JSON_VALUEを提案するための+1。これにより、生成された結果が、定義しない場合に発生する可能性があるxmlではなくjsonとして解析されるようになります
Sandeep Mandori 2018

11

これにハッシュマップを使用することもできます

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

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

PS。1つの値に対してのみ機能します


3

使用する ResponseEntity<ResponseBean>

ここでは、API応答を返すために、ResponseBeanまたは任意のJava Beanを使用できます。これは、ベストプラクティスです。応答に列挙型を使用しました。APIのステータスコードとステータスメッセージを返します。

@GetMapping(path = "/login")
public ResponseEntity<ServiceStatus> restApiExample(HttpServletRequest request,
            HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        loginService.login(username, password, request);
        return new ResponseEntity<ServiceStatus>(ServiceStatus.LOGIN_SUCCESS,
                HttpStatus.ACCEPTED);
    }

応答の場合ServiceStatusまたは(ResponseBody)

    public enum ServiceStatus {

    LOGIN_SUCCESS(0, "Login success"),

    private final int id;
    private final String message;

    //Enum constructor
    ServiceStatus(int id, String message) {
        this.id = id;
        this.message = message;
    }

    public int getId() {
        return id;
    }

    public String getMessage() {
        return message;
    }
}

Spring REST APIは、応答として以下のキーを持つ必要があります

  1. ステータスコード
  2. メッセージ

以下に最終応答があります

{

   "StatusCode" : "0",

   "Message":"Login success"

}

要件に応じて、ResponseBody(java POJO、ENUMなど)を使用できます。


2

より正確には、APIクエリのDTOを作成します(例:entityDTO)。

  1. エンティティのリストでのデフォルトの応答OK:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
    return entityService.getAllEntities();
}

ただし、異なるMapパラメーターを返す必要がある場合は、次の2つの例を使用でき
ます。2。mapのような1つのパラメーターを返す場合:

@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
    return ResponseEntity.status(HttpStatus.CREATED).body(
            Collections.singletonMap("key", "value"));
}
  1. また、いくつかのパラメーターの戻りマップが必要な場合(Java 9以降):
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getSomeParameters() {
    return ResponseEntity.status(HttpStatus.OK).body(Map.of(
            "key-1", "value-1",
            "key-2", "value-2",
            "key-3", "value-3"));
}

1

文字列を使用してJSONオブジェクトを返す必要がある場合は、次のように機能するはずです。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.ResponseEntity;
...

@RestController
@RequestMapping("/student")
public class StudentController {

    @GetMapping
    @RequestMapping("/")
    public ResponseEntity<JsonNode> get() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode json = mapper.readTree("{\"id\": \"132\", \"name\": \"Alice\"}");
        return ResponseEntity.ok(json);
    }
    ...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.