【问题标题】:Simplest way to decode and deserialize field whose value is a base64 encoded, stringified JSON blob with Jackson使用 Jackson 解码和反序列化值为 base64 编码的字符串化 JSON blob 的字段的最简单方法
【发布时间】:2020-04-25 22:39:42
【问题描述】:

在我们的 spring 数据项目中,我们使用“标准”方法来编写 DTO,其中我们使用 lombok 的 @Value@Builder 来实现不变性,并使用 @JsonDeserialize(builder = SomeClass.SomeClassBuilder.class) 来实现 jackson 反序列化。

这是一个最小的例子:

@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 blob 发送,例如:

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

如果您有任何关于如何使用此示例取得进展的想法,或者我可能缺少解决此问题的其他机制,我将不胜感激?

干杯

【问题讨论】:

    标签: java spring-boot jackson json-deserialization


    【解决方案1】:

    以真正的方式,在我提出问题几分钟后,我设法解决了我的问题。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-02
      • 2019-08-14
      相关资源
      最近更新 更多