Jackson Custom Serializer menunjukkan konteks yang sama untuk 2 bidang berbeda selama Serialisasi Json

Saya mencoba membuat JSON berdasarkan POJO kelas Objek saya. Untuk beberapa bidang, saya ingin menggunakan CustomSerializer karena saya ingin membuat bidang sesuai dengan kebutuhan saya. Oleh karena itu, saya telah membuat CustomSerializer.class.

CustomSerializer akan dipanggil oleh 2 bidang berbeda di POJO saya dan saya ingin menangani hal-hal secara berbeda berdasarkan bidang mana yang melakukan panggilan. Untuk salah satu bidang (extensions) saya ingin memiliki fieldName dan untuk bidang lainnya (withoutExtensions) saya tidak ingin memiliki fieldname di JSON saya.

Masalah yang saya hadapi adalah ketika CustomSerializer dipanggil maka saya mendapatkan fieldname yang sama untuk kedua panggilan tersebut karena itu saya tidak dapat membedakan bidang mana yang saat ini memanggil CustomSerializer.

Contoh kode berikut akan memberikan kejelasan lebih lanjut tentang masalah yang saya hadapi:

Customer Kelas POJO yang digunakan untuk membuat serial 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;
    }

}

Berikut ini adalah CustomSerializer saya yang akan dipanggil oleh 2 bidang (ekstensi dan tanpaEkstensi) selama pembuatan JSON:

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

Berikut ini adalah kelas Main saya yang akan membuat 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);
    }
}

Seperti yang bisa kita lihat ketika saya menjalankan aplikasi, CustomSerializer akan mencetak extensions dalam kedua kasus. Saya percaya itu harus mencetak extensions hanya sekali dan dalam kasus berikutnya ia harus memberikan withoutExtensions atau string kosong.

Saya hanya ingin tahu apakah ini bug di pihak Jackson atau adakah solusi yang bisa saya coba untuk membedakan bidang mana yang melakukan panggilan ke CustomSerializer saya.

Bantuan apa pun akan sangat dihargai. Terima kasih.


person BATMAN_2008    schedule 10.07.2021    source sumber


Jawaban (2)


A. Buat dua serialiser Map di mana yang satu membuat objek luar dan yang lainnya tidak

Kelebihan:

  • Mudah diterapkan
  • Mudah untuk diuji
  • Satu kelas melakukan satu hal
  • Map serialiser yang tidak membuat objek luar dapat diganti dengan Map serialiser khusus (jika memungkinkan)

Kontra:

  • Bisa menjadi masalah jika mereka perlu berbagi negara bagian.
  • Kemungkinan kode duplikat

B. Menerapkan ContextualSerializer antarmuka

Kelebihan:

  • Dapat dikonfigurasi untuk setiap bidang secara terpisah
  • Dapat berbagi status jika diperlukan. Pengguna mengontrol berapa banyak instance yang dibuat.

Kontra:

  • Melakukan lebih dari 1 hal
  • Bisa dengan mudah menjadi rumit

Contoh:

person Michał Ziober    schedule 10.07.2021
comment
Terima kasih banyak atas tanggapannya. Ini sangat membantu saya dan saya memilih opsi B karena menurut saya paling mudah untuk mengurangi kode yang berlebihan. Saya menerima jawaban ini dan sekali lagi terima kasih. Semoga harimu menyenangkan :) - person BATMAN_2008; 10.07.2021

Berdasarkan tanggapan dari @Michal saya memodifikasi kode dan berfungsi untuk kedua skenario tersebut. Memposting contoh kode lengkap karena dapat berguna bagi seseorang di masa depan:

Customer.class menambahkan @Extensions pada bidang yang wajib diisi:

@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();
}
person BATMAN_2008    schedule 10.07.2021