5

Spring boot 2.5

    @PostMapping("/cart/product")
    public Response addProduct(@RequestBody Map<String, Object> payloadMap) {
        logger.info("addProduct: payloadMap: " + payloadMap);
        String userName = payloadMap.get("user_name").toString();
        final Product product = new ObjectMapper().convertValue(payloadMap.get("product"), Product.class);
        int quantity = (int) payloadMap.get("quantity");
        Cart findCart = cartRepository.findByUsername(userName);
        if (findCart == null) {
            Cart cart = new Cart();
            cart.setCreated(new Date());
            cart.setUsername(userName);
            cart.addProduct(product, quantity);
            cartRepository.save(cart);
            logger.info("addProduct: success_add_product_to_new_cart: " + cart);
            return ResponseService.getSuccessResponse(GsonUtil.gson.toJsonTree(cart));
        } else {
            findCart.addProduct(product, quantity);
            logger.info("addProduct: before_save_exist_cart: " + findCart);
            cartRepository.save(findCart);
            logger.info("addProduct: success_add_product_to_exist_cart: " + findCart);
            return ResponseService.getSuccessResponse(GsonUtil.gson.toJsonTree(findCart));
        }
    }


public class ResponseService {
    private static final int SUCCESS_CODE = 0;
    private static final String SUCCESS_MESSAGE = "Success";
    private static final int ERROR_CODE = -1;

    private static Logger logger = LogManager.getLogger(ResponseService.class);

    public static Response getSuccessResponse(JsonElement body) {
        Response response = new Response(SUCCESS_CODE, SUCCESS_MESSAGE, body);
        logger.info("getSuccessResponse: response = " + response);
        return response;
    }


import com.google.gson.JsonElement;

public class Response {
    private int code;
    private String message;
    private JsonElement body;

    public Response(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public Response(int code, String message, JsonElement body) {
        this.code = code;
        this.message = message;
        this.body = body;
    }

But I get error when try to return response:

2020-04-12 12:02:18.825  INFO 9584 --- [nio-8092-exec-1] r.o.s.e.controllers.CartController       : addProduct: success_add_product_to_new_cart: Cart{id=130, username='admin@admin.com', created=Sun Apr 12 12:02:18 EEST 2020, updated=null, productEntities=[
ProductEntity{id=131, created=Sun Apr 12 12:02:18 EEST 2020, updated=null, quantity=1, orders=null, product=
Product{id=132, name='product name', description='product description', created=Tue Mar 10 22:34:15 EET 2020, updated=null, price=11.15, currency='EUR', images=[http://www.gravatar.com/avatar/44444?s=200x200&d=identicon, http://www.gravatar.com/avatar/33333?s=200x200&d=identicon]}}], totalAmount=11.15, currency='EUR'}
2020-04-12 12:02:18.836  INFO 9584 --- [nio-8092-exec-1] r.o.s.e.service.ResponseService          : getSuccessResponse: response = Response{code = 0, message = 'Success', body = '{"id":130,"username":"admin@admin.com","created":"Apr 12, 2020, 12:02:18 PM","productEntities":[{"id":131,"created":"Apr 12, 2020, 12:02:18 PM","quantity":1,"product":{"id":132,"name":"product name","description":"product description","created":"Mar 10, 2020, 10:34:15 PM","price":11.15,"currency":"EUR","images":["http://www.gravatar.com/avatar/44444?s=200x200&d=identicon","http://www.gravatar.com/avatar/33333?s=200x200&d=identicon"]}}],"totalAmount":11.15,"currency":"EUR"}'}
2020-04-12 12:02:18.861  WARN 9584 --- [nio-8092-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: JsonObject; nested exception is com.fasterxml.jackson.databind.JsonMappingException: JsonObject (through reference chain: ru.otus.software_architect.eshop_orders.api.Response["body"]->com.google.gson.JsonObject["asBoolean"])]
Alexei
  • 14,350
  • 37
  • 121
  • 240

3 Answers3

9

Spring Boot uses jackson by default to serialize json. In Response object you have field JsonElement body. It is object from gson package and jackson don't know how to serialize that.

Solution: add property (in the application.properties file) to use gson instead of jackson. Note that Spring Boot version is important.

Spring Boot >= 2.3.0.RELEASE: spring.mvc.converters.preferred-json-mapper=gson

Spring Boot < 2.3.0.RELEASE: spring.http.converters.preferred-json-mapper=gson

More informations:

Configuring Spring Boot to use Gson instead of Jackson

Spring Boot 2.3 Release Notes

2

Application.properties

spring.mvc.converters.preferred-json-mapper=gson
Asanka Sampath
  • 545
  • 4
  • 12
2

I've found a workaround by keeping Jackson but implementing my own Serializer for the classes that cause Jackson serialisation issue.

It's hackish solution but it's working now.

public class GSONObjectSerializer extends SimpleSerializers {

private static final long serialVersionUID = -8745250727467996655L;

private class EmptySerializer extends StdSerializer<Object> {
    
    /**
     * 
     */
    private static final long serialVersionUID = 5435498165882848947L;

    protected EmptySerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // --> Will write an empty JSON string
        gen.writeStartObject();
        gen.writeEndObject();
    }
}

@Override
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {

    // --> Here is to setup which class will be serialised by the custom serializer.
    if (type.isTypeOrSubTypeOf(JsonObject.class) || type.isTypeOrSubTypeOf(StripeResponse.class)) {
        return new EmptySerializer(type.getRawClass());
    }
    return super.findSerializer(config, type, beanDesc);
}

}

And you can register your serializer like this

@Configuration
public class SerialisationConfig {
    @Bean
    public ObjectMapper createMapper() {
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.setSerializers(new GSONObjectSerializer());
        return  Jackson2ObjectMapperBuilder.json().modules(Arrays.asList(simpleModule)).build();
    }
}
nono
  • 1,462
  • 1
  • 13
  • 21