Jackson Custom Serializer 在 Json 序列化期间显示 2 个不同字段的相同上下文

Jackson Custom Serializer shows the same context for 2 different field during the Json Serialization

我正在尝试基于我的对象 class POJO 创建一个 JSON。对于某些字段,我想使用 CustomSerializer,因为我想根据我的要求创建字段。因此,我创建了 CustomSerializer.class.

CustomSerializer 将由我的 POJO 中的 2 个不同字段调用,我想根据调用的字段不同来处理这些事情。对于其中一个字段 (extensions) 我想要 fieldName 而对于其他字段 (withoutExtensions) 我不希望在我的 JSON.

我面临的问题是,当调用 CustomSerializer 时,我得到的两个调用都是相同的 fieldname,因此我无法区分当前调用的是哪个字段CustomSerializer.

以下代码示例将更清楚地说明我面临的问题:

Customer POJO class 用于序列化 JSON:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
public class Customer {
    private String isA;
    private String name;

    @JsonSerialize(using = CustomSerializer.class)
    private Map<String, Object> extensions = new HashMap<>();

    private Map<String, Object> withoutExtensions = new HashMap<>();

    @JsonAnyGetter
    @JsonSerialize(using = CustomSerializer.class)
    public Map<String, Object> getWithoutExtensions() {
        return withoutExtensions;
    }

}

以下是我的 CustomSerializer,在创建 JSON 期间将由 2 个字段(扩展和​​ withoutExtensions)调用:

public class CustomSerializer extends JsonSerializer<Map<String, Object>> {

    @Override
    public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) {
        //I would like to create the outer object for "Extensions" but do not want to create outer object for "WithoutExtensions"
        
         System.out.println(gen.getOutputContext().getCurrentName());

        //In my case for both "Extensions" and "WithoutExtensions" i get the "currentName" as "Extensions" how can I ensure which field is calling this sealizer at
        // present
    }
}

以下是我的 Main class 它将创建一个 JSON:

public class Main {
    public static void main(String[] args) throws JsonProcessingException {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        final Customer customer = new Customer();

        customer.setName("Jackson");

        Map<String, Object> extensions = new HashMap<>();
        extensions.put("WithObject", "With");
        customer.setExtensions(extensions);

        Map<String, Object> withoutExtensions = new HashMap<>();
        extensions.put("WithoutObject", "Without");
        customer.setWithoutExtensions(withoutExtensions);

        final String eventAsJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
        System.out.println(eventAsJson);
    }
}

正如我们所见,当我 运行 应用程序时 CustomSerializer 将在两种情况下打印 extensions。我相信它应该只打印一次 extensions ,在下一种情况下它应该提供 withoutExtensions 或空字符串。

我只是想知道这是否是 Jackson 部分的错误,或者是否有任何解决方法可以尝试区分哪个字段正在调用我的 CustomSerializer

任何帮助将不胜感激。谢谢

一个。创建两个 Map 序列化程序,其中一个创建外部对象,另一个不创建

优点:

  • 易于实施
  • 易于测试
  • 一个class只做一件事
  • Map 不创建外部对象的序列化器可以替换为自定义 Map 序列化器(如果可能)

缺点:

  • 如果他们需要共享状态可能会有问题。
  • 可能是重复代码

乙。实现 ContextualSerializer 接口

优点:

  • 可以为每个字段单独配置
  • 如果需要可以共享状态。用户控制创建多少个实例。

缺点:

  • 做不止一件事
  • 很容易过于复杂

示例:

  • Need Jackson serializer for Double and need to specify precision at runtime

根据@Michal 的回复,我修改了代码,它适用于这两种情况。发布完整的代码示例,因为它对以后的人有帮助:

Customer.class 在必填字段上添加了 @Extensions

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
@NoArgsConstructor
public class Customer {
    private String isA;
    private String name;

    @JsonSerialize(using = CustomSerializer.class)
    @Extensions(extension = "extensions")
    private Map<String, Object> extensions = new HashMap<>();

    private Map<String, Object> withoutExtensions = new HashMap<>();

    @JsonAnyGetter
    @JsonSerialize(using = CustomSerializer.class)
    @Extensions(extension = "withoutExtensions")
    public Map<String, Object> getWithoutExtensions() {
        return withoutExtensions;
    }

}

CustomSerializer:

@NoArgsConstructor
public class CustomSerializer extends JsonSerializer<Map<String, Object>> implements ContextualSerializer {

    private String context = "";

    public CustomSerializer(String context) {
        this.context = context;
    }


    @Override
    public void serialize(Map<String, Object> value, JsonGenerator gen, SerializerProvider serializers) {
        if (this.context.equals("extensions")) {
            System.out.println("Extensions : " + this.context);
        } else if (this.context.equals("withoutExtensions")) {
            System.out.println("Without Extensions : " + this.context);
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        Extensions extensions = beanProperty.getAnnotation(Extensions.class);
        if (extensions != null) {
            return new CustomSerializer(extensions.extension());
        }
        return this;
    }
}


@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Extensions {
    String extension();
}