【问题标题】:Decode base64 encoded JSON to POJO with jackson and spring-boot使用 jackson 和 spring-boot 将 base64 编码的 JSON 解码为 POJO
【发布时间】:2017-11-06 09:04:18
【问题描述】:

我有一个这样的请求

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

其中 key varB 是一个 base64 编码的 JSON 字符串。像这样:

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

我想把它转换成一些这样的 POJO:

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

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

我经历了几种堆栈溢出的解决方案,例如 thisthis,但我想将 JSON 直接转换为 MyRequest 类型的对象,也许可以通过编写某种 Jackson 反序列化器。

如何使用 Jackson 和 Spring Boot 将 JSON 直接转换为 MyRequest

注意:

  1. POJO MyRequestB 非常大,B 可以进一步嵌套,因此手动操作将是一项艰巨的任务。
  2. 我无法控制发出的请求,它来自第三方来源。因此,无法更改请求的格式。

【问题讨论】:

  • 您是正确的,您需要编写自己的反序列化器并使用杰克逊注释@JsonDeserialize(using = foodeserilizer.class) 注册它。
  • 您能否建议我应该如何编写一个自定义反序列化器,它只解码 base64 编码的 JSON,然后使用默认的反序列化器?我找到了答案here,但是我应该如何要求默认反序列化器将解码后的字符串视为对象其余部分的 JSON?

标签: java json spring-mvc spring-boot jackson


【解决方案1】:

我做了一些实验,这里有一个简单的 Jackson Deserializer 应该适合你。

Deserializer 实现 ContextualDeserializer 接口以访问实际的 bean 属性(例如 varB)。检测正确的结果类型是必要的,因为反序列化器本身可以附加到任何类型的字段。

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

这是一个映射类的例子。

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

【讨论】:

  • 我注意到来的请求是application/x-www-form-urlencoded 格式,所以我不能在这里使用杰克逊序列化程序。接受您的答案,因为它适用于问题中提出的 Application/JSON。如果数据即将到来application/x-www-form-urlencoded,您能提出一些建议吗?
  • 你能提供一个请求的例子吗?键“varA”、“varB”在请求的顶层,还是被包装了?
  • 是的 varAvarB 是顶级的。像这样varB={base64encoded string here}&amp;varA="some string"。另外,我问了一个与此here 相关的单独问题。
  • 澄清一下,所以顶级对象是x-www-form-urlencodedvarB 包含 base64 编码的 JSON,对吗?
  • 这是真实发生的:developer.android.com/google/play/billing/rtdn-reference 所以感谢您的回答
猜你喜欢
  • 2012-11-05
  • 2014-05-24
  • 1970-01-01
  • 2020-07-29
  • 1970-01-01
  • 2023-03-09
  • 2015-07-14
  • 1970-01-01
  • 2018-02-22
相关资源
最近更新 更多