6

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='[email protected]', 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":"[email protected]","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"])]

3 Answers 3

14

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

Sign up to request clarification or add additional context in comments.

3 Comments

What if we still want to use Jackson and ignore the JsonObject ?
@nano You can just remove JsonObject field or change field type - You can try JsonNode from Jackson package.
The return object is provided by a third party library therefore, I can't remove that field.
4

Application.properties

spring.mvc.converters.preferred-json-mapper=gson

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.