Простейший способ декодировать и десериализовать поле, значение которого представляет собой закодированный в формате base64 блоб JSON с помощью Jackson - PullRequest
1 голос
/ 08 января 2020

В наших весенних проектах данных у нас есть «стандартный» подход к написанию наших DTO, где мы используем ломбок @Value и @Builder для неизменности и @JsonDeserialize(builder = SomeClass.SomeClassBuilder.class) для десериализации Джексона.

Вот минимальный пример:

@RestController
class Controller {
    @PostMapping("/post")
    void post(@RequestBody PostBody body) {
        System.out.println(body);
    }
}

@Value
@Builder
@JsonDeserialize(builder = PostBody.PostBodyBuilder.class)
class PostBody {

    byte[] id;

    ClientData clientData;

    @JsonPOJOBuilder(withPrefix = "")
    public static class PostBodyBuilder {}
}

@Value
@Builder
@JsonDeserialize(builder = ClientData.ClientDataBuilder.class)
class ClientData {
    String something;
    Integer somethingElse;


    @JsonPOJOBuilder(withPrefix = "")
    public static class ClientDataBuilder {}
}

Это работает так же хорошо, как и следовало ожидать, с нормальной JSON полезной нагрузкой, например:

{
  "id": "c29tZWlk",
  "clientData": {
    "something": "somethingValue",
    "somethingElse": 1
  }
}

Однако у нас есть вариант использования, где Структура clientData известна, но по причинам отправляется в виде двоичного кода в кодировке base64, например JSON, например:

{
  "id": "c29tZWlk",
  "clientData": "eyJzb21ldGhpbmciOiJzb21ldGhpbmdWYWx1ZSIsInNvbWV0aGluZ0Vsc2UiOjF9"
}

Было бы замечательно, если бы мы могли прозрачно декодировать и дешифровать Стрифицируйте это поле как часть десериализации PostBody, прежде чем он вызовет, запускает десериализатор для ClientData.

. Одним из решений является создание настраиваемого десериализатора для PostBody, но в реальном примере есть много дополнительные поля, которые затем необходимо обрабатывать вручную.

Я пытался создать собственный десериализатор ClientData, но я изо всех сил пытаюсь понять множество различных типов доступных интерфейсов десериализатора.

Пока у меня есть что-то вроде этого:

@Value
@Builder
@JsonDeserialize(builder = PostBody.PostBodyBuilder.class)
class PostBody {

    byte[] id;

    @JsonDeserialize(using = ClientDataBase64Deserializer.class)
    ClientData clientData;

    @JsonPOJOBuilder(withPrefix = "")
    public static class PostBodyBuilder {}
}

// SNIP

class ClientDataBase64Deserializer extends StdScalarDeserializer<ClientData> {

    protected ClientDataBase64Deserializer() {
        super(ClientData.class);
    }

    @Override
    public ClientData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        byte[] value = Base64.getDecoder().decode(jsonParser.getText());
        System.out.println(new String(value)); // prints stringified JSON
        jsonParser.setCurrentValue(/* somehow convert stringified JSON to a Tree Node? */ value);
        return deserializationContext.readValue(jsonParser, ClientData.class);
    }
}

Буду признателен за любые идеи о том, как продвигаться в этом примере, или какой-то другой механизм, который я могу упустить решить эту проблему?

Ура

1 Ответ

0 голосов
/ 08 января 2020

Таким образом, мне удалось решить мою проблему через несколько минут после того, как я задал вопрос.

Эта реализация ClientDataBase64Deserializer и PostBody работает, как и ожидалось:

@Value
@Builder
@JsonDeserialize(builder = PostBody.PostBodyBuilder.class)
public class PostBody {

    byte[] id;

    ClientData clientData;

    public interface IPostBodyBuilder {
        @JsonDeserialize(using = ClientDataBase64Deserializer.class)
        PostBody.PostBodyBuilder clientData(ClientData clientData);
    }

    @JsonPOJOBuilder(withPrefix = "")
    public static class PostBodyBuilder implements IPostBodyBuilder {}
}


class ClientDataBase64Deserializer extends StdScalarDeserializer<ClientData> {
    private final ObjectMapper objectMapper;

    protected ClientDataBase64Deserializer(ObjectMapper objectMapper) {
        super(ClientData.class);
        this.objectMapper = objectMapper;
    }

    @Override
    public ClientData deserialize(
            JsonParser jsonParser, DeserializationContext deserializationContext
    ) {
        byte[] value = jsonParser.readValueAs(byte[].class);
        return objectMapper.readValue(value, ClientData.class);
    }
}

...