Jackson的自定义序列化器调用默认序列化器详解

1. 简介

将我们完整的数据结构序列化为JSON时使用所有字段的精确一对一表示有时可能不合适,或者根本可能不是我们想要的。 相反,我们可能想要创建数据的扩展或简化视图。 这就是自定义 Jackson 序列化程序发挥作用的地方。

但是,实现自定义序列化程序可能很乏味,尤其是当我们的模型对象有很多字段、集合或嵌套对象时。 幸运的是,Jackson 库 有几种方式可以让这项工作变得更简单。

在本文中,我们将看看自定义 Jackson 序列化器,并展示如何在自定义序列化器中访问默认序列化器。

2. 示例数据模型

在深入研究 Jackson 的自定义序列化器之前,让我们先看看我们想要序列化的示例 Folder 类:

public class Folder {
    private Long id;
    private String name;
    private String owner;
    private Date created;
    private Date modified;
    private Date lastAccess;
    private List<File> files = new ArrayList<>();

    // standard getters and setters
}

还有 File 类,它在我们的 Folder 类中定义为 List

public class File {
    private Long id;
    private String name;

    // standard getters and setters
}

3. Jackson中的自定义序列化器

使用自定义序列化器的主要优点是我们不必修改我们的类结构。 另外,我们可以轻松地将我们的预期行为与类本身分离。

所以,让我们想象一下,我们想要一个 Folder 类的简化视图:

{
    "name": "Root Folder",
    "files": [
        {"id": 1, "name": "File 1"},
        {"id": 2, "name": "File 2"}
    ]
}

正如我们将在接下来的部分中看到的,有几种方法可以在 Jackson 中实现我们想要的输出。

3.1. 蛮力方法

首先,不使用 Jackson 的默认序列化器,我们可以创建一个自定义序列化器,我们自己完成所有繁重的工作。

让我们为我们的 Folder 类创建一个自定义序列化器来实现这一点:

public class FolderJsonSerializer extends StdSerializer<Folder> {

    public FolderJsonSerializer() {
        super(Folder.class);
    }

    @Override
    public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider)
      throws IOException {
        gen.writeStartObject();
        gen.writeStringField("name", value.getName());

        gen.writeArrayFieldStart("files");
        for (File file : value.getFiles()) {
            gen.writeStartObject();
            gen.writeNumberField("id", file.getId());
            gen.writeStringField("name", file.getName());
            gen.writeEndObject();
        }
        gen.writeEndArray();

        gen.writeEndObject();
    }
}

因此,我们可以将我们的 Folder 类序列化为仅包含我们想要的字段的简化视图。

3.2. 使用内在的ObjectMapper

尽管自定义序列化器为我们提供了详细更改每个属性的灵活性,但我们可以通过重用 Jackson 的默认序列化器来简化工作。

使用默认序列化器的一种方法是访问内在的 ObjectMapper 类:

@Override
public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeStartObject();
    gen.writeStringField("name", value.getName());

    ObjectMapper mapper = (ObjectMapper) gen.getCodec();
    gen.writeFieldName("files");
    String stringValue = mapper.writeValueAsString(value.getFiles());
    gen.writeRawValue(stringValue);

    gen.writeEndObject();
}

因此,Jackson 只是通过序列化 File 对象的 List 来处理繁重的工作,然后我们的输出将是相同的。

3.3. 使用 SerializerProvider

调用默认序列化器的另一种方法是使用 SerializerProvider。因此,我们将过程委托给 File 类型的默认序列化程序。

现在,让我们在 SerializerProvider 的帮助下稍微简化一下我们的代码:

@Override
public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeStartObject();
    gen.writeStringField("name", value.getName());

    provider.defaultSerializeField("files", value.getFiles(), gen);

    gen.writeEndObject();
}

而且,就像以前一样,我们得到相同的输出。

4. 一个可能的递归问题

根据用例,我们可能需要通过包含 Folder 的更多详细信息来扩展我们的序列化数据。 这可能是为了集成一个遗留系统或一个我们没有机会修改的外部应用程序。

让我们更改我们的序列化器,为我们的序列化数据创建一个 details 字段,以简单地公开 Folder 类的所有字段:

@Override
public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeStartObject();
    gen.writeStringField("name", value.getName());

    provider.defaultSerializeField("files", value.getFiles(), gen);

    // this line causes exception
    provider.defaultSerializeField("details", value, gen);

    gen.writeEndObject();
}

这次我们得到一个 StackOverflowError 异常。

当我们定义一个自定义序列化器时,Jackson 在内部覆盖了为 Folder 类型创建的原始 BeanSerializer 实例。因此,我们的 SerializerProvider 每次都会找到自定义的序列化器,而不是默认的序列化器,这会导致无限循环。

那么,我们如何解决这个问题呢? 我们将在下一节中看到适用于这种情况的一个可用解决方案。

5. 使用 BeanSerializerModifier

一种可能的解决方法是使用 BeanSerializerModifier 在 Jackson 内部覆盖它之前存储 Folder 类型的默认序列化程序。

让我们修改我们的序列化器并添加一个额外的字段 - defaultSerializer

private final JsonSerializer<Object> defaultSerializer;

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

接下来,我们将创建一个 BeanSerializerModifier 的实现来传递默认的序列化器:

public class FolderBeanSerializerModifier extends BeanSerializerModifier {

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

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

        return serializer;
    }
}

现在,我们需要将 BeanSerializerModifier 注册为模块以使其工作:

ObjectMapper mapper = new ObjectMapper();

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

mapper.registerModule(module);

然后,我们将 defaultSerializer 用于 details 字段:

@Override
public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    gen.writeStartObject();
    gen.writeStringField("name", value.getName());

    provider.defaultSerializeField("files", value.getFiles(), gen);

    gen.writeFieldName("details");
    defaultSerializer.serialize(value, gen, provider);

    gen.writeEndObject();
}

最后,我们可能希望从 details 中删除 files 字段,因为我们已经将它单独写入序列化数据中。

因此,我们只需忽略 Folder 类中的 files 字段:

@JsonIgnore
private List<File> files = new ArrayList<>();

最后,问题解决了,我们也得到了预期的输出:

{
    "name": "Root Folder",
    "files": [
        {"id": 1, "name": "File 1"},
        {"id": 2, "name": "File 2"}
    ],
    "details": {
        "id":1,
        "name": "Root Folder",
        "owner": "root",
        "created": 1565203657164,
        "modified": 1565203657164,
        "lastAccess": 1565203657164
    }
}

<<<<<<<<<<<< [完] >>>>>>>>>>>>

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Jackson是一个Java库,用于将Java对象序列化JSON格式和反序列化Java对象。要自定义Jackson的反序列化,可以使用@JsonDeserialize注解并传入自定义序列化类,如下所示: ``` @JsonDeserialize(using = MyDeserializer.class) public class MyObject { // class fields and methods } ``` MyDeserializer类需要继承com.fasterxml.jackson.databind.JsonDeserializer<T>,并重写deserialize()方法实现自定义序列化逻辑。 ``` public class MyDeserializer extends JsonDeserializer<MyObject> { @Override public MyObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { // custom deserialization logic } } ``` 注意: 类需要添加 Jackson 依赖 ``` <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.3</version> </dependency> ``` ### 回答2: 要自定义Jackson的反序列化,可以按照以下步骤操作: 1. 创建自定义序列化类:首先创建一个新的类,实现Jackson提供的`JsonDeserializer`接口。该接口要求实现`deserialize`方法,该方法将负责将JSON数据转换为目标Java对象。 2. 实现`deserialize`方法:在`deserialize`方法中,根据需要的逻辑解析JSON数据并创建目标对象,并将其返回。 3. 注册自定义序列化:在使用Jackson的代码中,需要将自定义序列化注册到`ObjectMapper`中,以便在反序列化时使用。可以使用`SimpleModule`类创建一个新的模块,并使用`addDeserializer`方法将自定义序列化与目标类型关联起来。然后,使用`registerModule`方法将模块注册到`ObjectMapper`中。 4. 使用自定义序列化:在需要使用自定义序列化的代码中,创建`ObjectMapper`实例,并执行反序列化操作。在反序列化时,Jackson将自动选择正确的反序列化进行处理。 通过以上步骤,我们可以自定义Jackson的反序列化,并在需要时将其应用于特定的对象类型。这样可以根据自定义的逻辑将JSON数据转换为目标对象,使得反序列化的过程更加灵活和个性化。 ### 回答3: 要自定义 Jackson序列化,你可以按照以下步骤进行操作: 1. 创建一个类,并实现 Jackson 的 `JsonDeserializer` 接口。这个接口要求你实现 `deserialize` 方法,用于自定义对象的反序列化逻辑。 2. 在 `deserialize` 方法中,你可以根据需要读取 JSON 中的属性并进行处理。你可以使用 Jackson 的 `JsonParser` 对象来读取 JSON 数据。 3. 根据 JSON 中的属性,你可以创建一个自定义的对象,并将属性值填充进去。 4. 在自定义对象中,你可以添加任何你需要的验证逻辑,以确保对象的属性是有效的。 5. 最后,返回你自定义的对象。 6. 使用自定义的反序列化,你可以为 Jackson 提供一个 `ObjectMapper`,并使用 `registerModule` 方法注册你的自定义模块。 以下是一个示例代码,用来自定义一个反序列化: ```java public class CustomDeserializer extends JsonDeserializer<CustomObject> { @Override public CustomObject deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); // 从 JSON 中读取属性 String property1 = jsonNode.get("property1").asText(); int property2 = jsonNode.get("property2").asInt(); // 创建自定义对象并填充属性 CustomObject customObject = new CustomObject(); customObject.setProperty1(property1); customObject.setProperty2(property2); // 进行一些验证逻辑 if (customObject.getProperty1() == null || customObject.getProperty1().isEmpty()) { throw new IOException("Invalid value for property1"); } return customObject; } } ``` 使用自定义的反序列化,你可以在 ObjectMapper 中注册你的自定义模块: ```java ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(CustomObject.class, new CustomDeserializer()); objectMapper.registerModule(module); CustomObject customObject = objectMapper.readValue(jsonString, CustomObject.class); ``` 通过以上步骤,你就可以成功自定义 Jackson 的反序列化,并根据你的需求对对象进行反序列化操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱游泳的老白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值