user3529850
user3529850

Reputation: 986

How to setup jackson deserialize for base64-encoded string to object inside the controller

Environment:
Spring-boot: 2.0.5.RELEASE

The problem:
Given:

@Transactional(readOnly = true)
@GetMapping(value = "/list")
public ResponseEntity<List<CarDTO>> getList(@RequestParam final InteriorDTO interior) {
    return ResponseEntity.ok(getCarsBy(interior));
}

DTO

public class InteriorDTO extends DetailsDTO {
}

public class DetailsDTO {
    private int listSize;
}

When:
GET /cars/list?interior=eyJsaXN0U2l6ZSI6NX0=

(eyJsaXN0U2l6ZSI6NX0= is base64 encoded: {"listSize":5})
Then:

Failed to convert value of type 'java.lang.String' to required type '(...).InteriorDTO'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type '(...).InteriorDTO': no matching editors or conversion strategy found

The question:
How to properly define deserializer for jackson to make him handle that case ? should it be something like this (doesn't work for me) ?

Upvotes: 1

Views: 2892

Answers (1)

k0staa
k0staa

Reputation: 342

You must create custom Spring Converter:


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class Base64InteriorToDtoConverter implements Converter<String, InteriorDTO> {

    @Override
    public InteriorDTO convert(String source) {
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] valueDecoded = Base64.decodeBase64(source);
        InteriorDTO interiorDTO = null;
        try {
            interiorDTO = objectMapper.readValue(new String(valueDecoded), InteriorDTO.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return interiorDTO;
    }
}

(Just remember about proper exception handling)

For base64 decoding I used the Apache Commons Codec library, my build.gradle file:

plugins {
    id 'org.springframework.boot' version '2.2.2.RELEASE'
    id 'io.spring.dependency-management' version '1.0.8.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'commons-codec:commons-codec:1.13'

    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

test {
    useJUnitPlatform()
}

Jackson is used to map the String value to the object. Remember about setters in the DetailsDTO class:

public class DetailsDTO {
    private int listSize;

    public int getListSize() {
        return listSize;
    }

    public void setListSize(int listSize) {
        this.listSize = listSize;
    }
}

Upvotes: 1

Related Questions