Decode base64 encoded JSON to POJO with jackson and spring-boot

10,148

I've done some experiments and here is a simple Jackson Deserializer which should work for you.

Deserializer implements the ContextualDeserializer interface to get access to actual bean property (for example varB). It's necessary for detecting correct result type, because deserializer itself can be attached to a field of any type.

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;

import java.io.IOException;
import java.util.Base64;

public class Base64Deserializer extends JsonDeserializer<Object> implements ContextualDeserializer {

    private Class<?> resultClass;

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext context, BeanProperty property) throws JsonMappingException {
        this.resultClass = property.getType().getRawClass();
        return this;
    }

    @Override
    public Object deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
        String value = parser.getValueAsString();
        Base64.Decoder decoder = Base64.getDecoder();

        try {
            ObjectMapper objectMapper = new ObjectMapper();
            byte[] decodedValue = decoder.decode(value);

            return objectMapper.readValue(decodedValue, this.resultClass);
        } catch (IllegalArgumentException | JsonParseException e) {
            String fieldName = parser.getParsingContext().getCurrentName();
            Class<?> wrapperClass = parser.getParsingContext().getCurrentValue().getClass();

            throw new InvalidFormatException(
                parser,
                String.format("Value for '%s' is not a base64 encoded JSON", fieldName),
                value,
                wrapperClass
            );
        }
    }
}

Here is an example of mapped class.

public class MyRequest {

    private String varA;

    @JsonDeserialize(using = Base64Deserializer.class)
    private B varB;

    public String getVarA() {
        return varA;
    }

    public void setVarA(String varA) {
        this.varA = varA;
    }

    public B getVarB() {
        return varB;
    }

    public void setVarB(B varB) {
        this.varB = varB;
    }
}
Share:
10,148
Saheb
Author by

Saheb

Undergraduate student from computer science.

Updated on June 22, 2022

Comments

  • Saheb
    Saheb about 2 years

    I have a request coming like this

    {
        "varA"  : "A",
        "varB" : "TCFNhbiBKb3NlMRgwFgYDVQQK"
    }
    

    where key varB is a base64 encoded JSON string. something like this:

    {
        "nestedVarB1": "some value here",
        "nestedVarB2" : "some other value here"
    }
    

    I want to convert it into some POJOs like this:

    @Data
    public class MyRequest {
        private String varA;
        private B varB;
    }
    
    @Data
    public class B {
        private String nestedVarB1;
        private String nestedVarB2;
    }
    

    I went through several solutions on stack overflow like this and this, but I want to convert the JSON directly into an object of type MyRequest maybe by writing some sort of Jackson deserializer.

    How can I convert the JSON directly into MyRequest using Jackson and Spring Boot?

    Note:

    1. POJOs MyRequest and B are very big and B could be further nested, so doing it manually would be a big task.
    2. I don't have any control over the request coming, it is coming from a third party source. So, can't change the format of the request.
  • Saheb
    Saheb about 7 years
    I noticed that the request coming is in application/x-www-form-urlencoded format so I can't use Jackson serializer here. Accepting your answer as it works fine for Application/JSON asked in the question. Can you suggest something if data is coming application/x-www-form-urlencoded?
  • Lukáš Kováč
    Lukáš Kováč about 7 years
    Can you please provide an example of request? Keys "varA", "varB" are on the top level of the request, or they are wrapped?
  • Saheb
    Saheb about 7 years
    yes varA and varB are top level. Something like this varB={base64encoded string here}&varA="some string". Also, I have asked a separate question related to this here.
  • Lukáš Kováč
    Lukáš Kováč about 7 years
    just to clarify, so top level object comes as x-www-form-urlencoded, with varB which contains base64 encoded JSON, correct?
  • kris larson
    kris larson over 2 years
    This happens for real: developer.android.com/google/play/billing/rtdn-reference so thanks for this answer