JSON nesnesini Spring Boot'da yanıt olarak döndürme


88

Spring Boot'ta örnek bir RestController'ım var:

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

JSON kitaplığını kullanıyorum org.json

API'ye /hellogirdiğimde şöyle bir istisna alıyorum:

Sunucu uygulaması [dispatcherServlet] için Servlet.service (), [] yolu bağlamında istisna attı [İstek işleme başarısız oldu; iç içe geçmiş istisna java.lang.IllegalArgumentException: Temel nedene sahip sınıf org.json.JSONObject] türünün dönüş değeri için dönüştürücü bulunamadı

java.lang.IllegalArgumentException: türünün dönüş değeri için dönüştürücü bulunamadı: sınıf org.json.JSONObject

Sorun ne? Birisi tam olarak ne olduğunu açıklayabilir mi?


Jackson, JSONObject'i json'a dönüştüremez.
Pau

Tamam, anlıyorum. Bunu düzeltmek için ne yapılabilir?
iwekesi

1
Yanıtın anında oluşturulmasını istiyorum. Her yanıt için belirli sınıflar oluşturmak istemiyorum.
iwekesi

2
Yönteminizin String olarak dönmesi daha iyi olabilir. Ek olarak, yönteme @ResponseBody ek açıklamasını da ekleyebilirsiniz, bu, yanıtınızı istendiği gibi işleyecektir :-)@GetMapping(path = "/hello") @ResponseBody public String sayHello() {return"{'aa':'bb'}";}
vegaasen

@vegaasen ResponseBody hakkında biraz ayrıntı verebilir misin
iwekesi

Yanıtlar:


109

Spring Boot web'i kullandığınız için, Jackson bağımlılığı örtüktür ve açık bir şekilde tanımlamamız gerekmez. pom.xmlEclipse kullanıyorsanız, bağımlılık hiyerarşisi sekmesinden Jackson bağımlılığını kontrol edebilirsiniz .

Ve ile @RestControlleraçıklamış olduğunuz gibi, açık json dönüşümü yapmaya gerek yoktur. Sadece bir POJO iade edin ve jackson serileştirici json'a dönüştürme işlemini halledecektir. @ResponseBody@Controller ile kullanıldığında kullanmaya eşdeğerdir . Vanilya yerine yerleştirdiğimiz @ResponseBodyher denetleyici yöntemine yerleştirmek @RestControlleryerine @Controllerve @ResponseBodyvarsayılan olarak bu denetleyicideki tüm kaynaklara uygulanır.
Bu bağlantıya bakın: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-responsebody

Karşılaştığınız sorun, döndürülen nesnenin (JSONObject) belirli özellikler için alıcıya sahip olmamasıdır. Ve niyetiniz bu JSONObject'i serileştirmek değil, bunun yerine bir POJO'yu serileştirmek. Öyleyse sadece POJO'yu iade edin.
Bu bağlantıya bakın: https://stackoverflow.com/a/35822500/5039001

Bir json serileştirilmiş dizge döndürmek istiyorsanız, dizeyi döndürmeniz yeterlidir. Spring, bu durumda JSON dönüştürücü yerine StringHttpMessageConverter'ı kullanacaktır.


json dizesi javadan dönmek istediğiniz şeyse, o zaman json serileştirilmişse bir dizge döndürebilirsiniz. Dizeyi json'a ve json'u dizeye dönüştürmeye gerek yok.
prem kumar

6
Sert bir derleme zamanı yapısı olmadan bir dizi ad-değer çifti döndürmek istiyorsanız, bir Map<String,Object>veya bir Propertiesnesne döndürebilirsiniz
Vihung

@prem kumar rastgele soru: 'vanilla Controller ve ResponseBody yerine' ne demek istiyorsun? vanilya burada ne var?
Orkun Özen

Alışılmış bir Denetleyiciyi kastettim ve her istek yöntemine ResponseBody ek açıklaması yerleştirilmişti.
prem kumar

68

Mevcut yaklaşımınızın işe yaramamasının nedeni, Jackson'ın varsayılan olarak nesneleri serileştirmek ve seri durumdan çıkarmak için kullanılmasıdır. Ancak JSONObject,. Dinamik bir JSON yapısı oluşturmak istiyorsanız Map, örneğin bir kullanabilirsiniz :

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

Bu, aşağıdaki JSON yanıtına yol açacaktır:

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

Bu biraz sınırlıdır, çünkü alt nesneler eklemek biraz daha zor olabilir. Jackson kullanarak olsa kendi mekanizmasına sahiptir ObjectNodeve ArrayNode. Kullanmak için, ObjectMapperhizmetinize / denetleyicinize otomatik kablolama yapmanız gerekir . O zaman şunları kullanabilirsiniz:

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

Bu yaklaşım, alt nesneler, diziler eklemenize ve tüm çeşitli türleri kullanmanıza olanak tanır.


2
burada haritacı nedir?
iwekesi

1
@iwekesi bu, otomatik olarak bağlamanız gereken Jackson'dır ObjectMapper(son kod parçamın üstündeki paragrafa bakın).
g00glen00b

1
Edilir çarpıcı bir anlamlı JSON nesneleri üretmek için böyle uzunlukları gitmek zorunda olduğunu bilmek! Pivotal'ın bu sınırlamaları belirtmek için hiç çaba göstermemesi de üzücü ( spring.io/guides/gs/actuator-service ). Neyse ki, bizde SO var! ;)
cogitoergosum

@HikaruShindo java.util.HashMap, Java 1.2'den beri Java'nın temel bir işlevidir.
g00glen00b

44

String@Vagaasen tarafından önerildiği gibi bir yanıt döndürebilir veya ResponseEntityaşağıdaki gibi Spring tarafından sağlanan Nesneyi kullanabilirsiniz . Bu şekilde Http status code, web servis çağrısında daha yararlı olan geri dönebilirsiniz .

@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
Varlıklara JSONObject eklersem, yine benzer bir istisna veriyor
iwekesi

@Sangam cevabınız, jackson-dataformat-xml ile ilgili başka bir sorun için bana yardımcı oldu
divine

Bu çok yardımcı oldu! Teşekkür ederim!
jones-chris

1
Bu cevaba neden daha fazla oy verilmediğini merak ediyorum. Ben de Spring'de yeniyim, bu yüzden bunun iyi bir yazılım mühendisliği uygulaması olup olmadığını bilmiyorum. Bununla birlikte, bu cevap bana gerçekten yardımcı oldu. Bununla birlikte, a ile çok sorun yaşadım JSONObject, ancak Spring Jackson'ı kullandığından beri onu bir HashMapyerine a olarak değiştirdim ve sonra bu cevapta okuduğum kod işe yaradı.
Melvin Roest

2
MediaType.APPLICATION_JSON_VALUE önermek için +1, çünkü üretilen sonucun json olarak değil, tanımlamazsanız olabileceği gibi xml olarak ayrıştırılmasını sağlar
Sandeep Mandori

11

bunun için bir hashmap de kullanabilirsiniz

@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. Yalnızca 1 değer için çalışır


3

kullanım ResponseEntity<ResponseBean>

API yanıtınızı döndürmek için burada ResponseBean veya Any java bean kullanabilirsiniz ve bu en iyi uygulamadır. Yanıt için Enum kullandım. API'nin durum kodunu ve durum mesajını döndürür.

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

yanıt için ServiceStatus veya (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 yanıt olarak aşağıdaki anahtara sahip olmalıdır

  1. Durum kodu
  2. İleti

aşağıda son yanıtı alacaksınız

{

   "StatusCode" : "0",

   "Message":"Login success"

}

ResponseBody'yi (java POJO, ENUM, vb.) ihtiyacınıza göre kullanabilirsiniz.


2

API sorguları için daha doğru DTO oluşturma, örneğin entityDTO:

  1. Varsayılan yanıt OK varlık listesiyle:
@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.OK)
public List<EntityDto> getAll() {
    return entityService.getAllEntities();
}

Ancak, farklı Harita parametreleri döndürmeniz gerekirse, sonraki iki örneği kullanabilirsiniz
2. Harita gibi bir parametre döndürmek için:

@GetMapping(produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getOneParameterMap() {
    return ResponseEntity.status(HttpStatus.CREATED).body(
            Collections.singletonMap("key", "value"));
}
  1. Ve bazı parametrelerin dönüş haritasına ihtiyacınız varsa (Java 9'dan beri):
@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

Bir Dize kullanarak bir JSON nesnesi döndürmeniz gerekiyorsa, aşağıdakiler çalışmalıdır:

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);
    }
    ...
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.