java - Jackson 将 Object 序列化为 JSON 到 base64(没有无限循环)

标签 java json jackson base64 jackson-databind

有没有一种简单的方法可以使用 Jackson 将对象序列化为 base64 编码的 JSON? (对象-> JSON-> base64)

我尝试使用自定义 StdSerializer ,但这(当然)会导致无限循环:

class MySerializer extends StdSerializer<Foo> {
  public void serialize(Foo value, JsonGenerator gen, SerializerProvider provider) {
    StringWriter stringWriter = new StringWriter();
    JsonGenerator newGen = gen.getCodec().getFactory().createGenerator(stringWriter);
    gen.getCodec().getFactory().getCodec().writeValue(newGen, value);
    String json = stringWriter.toString();
    String base64 = new String(Base64.getEncoder().encode(json.getBytes()));
    gen.writeString(base64);
  }
}

一种解决方法是将所有字段复制到另一个类并使用该类作为中间表示:

class TmpFoo {
  public String field1;
  public int field2;
  // ...
}

class MySerializer extends StdSerializer<Foo> {
  public void serialize(Foo value, JsonGenerator gen, SerializerProvider provider) {
    TmpFoo tmp = new TmpFoo();
    tmp.field1 = value.field1;
    tmp.field2 = value.field2;
    // etc.

    StringWriter stringWriter = new StringWriter();
    JsonGenerator newGen = gen.getCodec().getFactory().createGenerator(stringWriter);
    gen.getCodec().getFactory().getCodec().writeValue(newGen, tmp); // here "tmp" instead of "value"
    String json = stringWriter.toString();
    String base64 = new String(Base64.getEncoder().encode(json.getBytes()));
    gen.writeString(base64);
  }
}

创建 new ObjectMapper不需要,因为我需要默认 ObjectMapper 的所有已注册模块和序列化程序。

我希望有一些更简单的方法来实现这一点。

编辑:示例

第 1 步:Java 对象

class Foo {
  String field1 = "foo";
  int field2 = 42;
}

第 2 步:JSON
{"field1":"foo","field2":42}

第三步:Base64
eyJmaWVsZDEiOiJmb28iLCJmaWVsZDIiOjQyfQ==

最佳答案

根据this site ,有一种解决方法可以避免这种递归问题:

When we define a custom serializer, Jackson internally overrides the original BeanSerializer instance [...] our SerializerProvider finds the customized serializer every time, instead of the default one, and this causes an infinite loop.

A possible workaround is using BeanSerializerModifier to store the default serializer for the type Folder before Jackson internally overrides it.



如果我正确理解了解决方法,您的 Serializer应该是这样的:

class FooSerializer extends StdSerializer<Foo> {

    private final JsonSerializer<Object> defaultSerializer;

    public FooSerializer(JsonSerializer<Object> defaultSerializer) {
        super(Foo.class);
        this.defaultSerializer = defaultSerializer;
    }

    @Override
    public void serialize(Foo value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        StringWriter stringWriter = new StringWriter();
        JsonGenerator tempGen = provider.getGenerator().getCodec().getFactory().createGenerator(stringWriter);
        defaultSerializer.serialize(value, tempGen, provider);

        tempGen.flush();

        String json = stringWriter.toString();
        String base64 = new String(Base64.getEncoder().encode(json.getBytes()));
        gen.writeString(base64);
    }
}

除了序列化器,还需要一个修饰符:

public class FooBeanSerializerModifier extends BeanSerializerModifier {

    @Override
    public JsonSerializer<?> modifySerializer(
      SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {

        if (beanDesc.getBeanClass().equals(Foo.class)) {
            return new FooSerializer((JsonSerializer<Object>) serializer);
        }
        return serializer;
    }
}

示例模块:

ObjectMapper mapper = new ObjectMapper();

SimpleModule module = new SimpleModule();
module.setSerializerModifier(new FooBeanSerializerModifier());

mapper.registerModule(module);

编辑:

我已添加 flush()冲洗JsonGenerator tempGen .
另外,我使用 JUnit 创建了一个最小的测试环境,它使用 Foo 验证您的示例: github repo 可以找到here .

编辑:备选方案 2

另一个(简单)选项是使用带有泛型的包装类:

public class Base64Wrapper<T> {

    private final T wrapped;

    private Base64Wrapper(T wrapped) {
        this.wrapped = wrapped;
    }

    public T getWrapped() {
        return this.wrapped;
    }

    public static <T> Base64Wrapper<T> of(T wrapped) {
        return new Base64Wrapper<>(wrapped);
    }
}

public class Base64WrapperSerializer extends StdSerializer<Base64Wrapper> {


    public Base64WrapperSerializer() {
        super(Base64Wrapper.class);
    }

    @Override
    public void serialize(Base64Wrapper value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        StringWriter stringWriter = new StringWriter();
        JsonGenerator tempGen = provider.getGenerator().getCodec().getFactory().createGenerator(stringWriter);
        provider.defaultSerializeValue(value.getWrapped(), tempGen);
        tempGen.flush();

        String json = stringWriter.toString();
        String base64 = new String(Base64.getEncoder().encode(json.getBytes()));
        gen.writeString(base64);
    }
}

一个示例用例是:

final ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(new Base64WrapperSerializer());
mapper.registerModule(module);

final Foo foo = new Foo();
final Base64Wrapper<Foo> base64Wrapper = Base64Wrapper.of(foo);
final String base64Json = mapper.writeValueAsString(base64Wrapper);

这个例子可以在 this GitHub 中找到。 (branch: wrapper) repo,使用 JUnit 测试从您的 foo 示例中验证您的 BASE64 字符串。

关于java - Jackson 将 Object 序列化为 JSON 到 base64(没有无限循环),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61706080/

相关文章:

java - 我正在恢复一个数据库,我需要在我的 Java 应用程序中锁定它的任何 Activity

java - 将 json 数组反序列化为列表并保留列表中 json 的数组顺序

java - 在同一页中打印两个 JPanel

java - JACOB 库在多线程中使用时失败

java - IDE 不显示 Lombok 为 Jackson 注解的类生成的 getter 和 setter

json - 在 postgresql 查询中展平 jsonb 对象的更好方法是什么

java - 使用 JsonInclude 注解忽略扩展类中的空值

json - @XmlJavaTypeAdapter 不适用于 Jackson JSON

java - 提供具有相同文件分割的 map 分割

javascript - 如何在 native react 中添加状态