Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Eliminating Bean–JSON Conversions by Backing DTOs with Vert.x JsonObject

Tech 2

Frequent bean-to-JSON and JSON-to-bean transformations introduce unnecessary CPU and GC overhead when Vert.x EventBus requires messages in io.vertx.core.json.JsonObject/JsonArray. Instead of serializing/deserializing on every hop, keep DTO state directly in a JsonObject and expose JavaBean-style accessors for framework compatibility. Shared utilities (date encoding/decoding, list bridging) live in a common base class.

Key points:

  • Replace POJO fields with a JsonObject payload.
  • Keep getters/setters so dependency injection and validation farmeworks still work.
  • Centralize non-String handling (e.g., LocalDateTime/LocalDate) in the base class.
  • Provide helpers to convert lists of DTOs to JsonArray and vice versa without materializing intermediate JSON strings.

Code: base class that stores data in JsonObject

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class JsonBackedDto {

    private static final Logger LOG = LoggerFactory.getLogger(JsonBackedDto.class);

    private static final DateTimeFormatter TS_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @JsonIgnore
    protected JsonObject data;

    public JsonBackedDto() {
        this.data = new JsonObject();
    }

    public JsonBackedDto(JsonObject source) {
        this.data = source == null ? new JsonObject() : source;
    }

    public JsonObject json() {
        return data;
    }

    public void setJson(JsonObject source) {
        this.data = source == null ? new JsonObject() : source;
    }

    // Date/time helpers
    public LocalDateTime readDateTime(String key) {
        String v = data.getString(key);
        return v == null || v.isEmpty() ? null : LocalDateTime.parse(v, TS_FMT);
    }

    public LocalDate readDate(String key) {
        String v = data.getString(key);
        return v == null || v.isEmpty() ? null : LocalDate.parse(v, DATE_FMT);
    }

    public void writeDateTime(String key, LocalDateTime value) {
        data.put(key, value == null ? null : value.format(TS_FMT));
    }

    public void writeDate(String key, LocalDate value) {
        data.put(key, value == null ? null : value.format(DATE_FMT));
    }

    // Convenience conversions for lists of DTOs
    public static <T extends JsonBackedDto> JsonArray toJsonArray(List<T> items) {
        if (items == null) return null;
        JsonArray arr = new JsonArray();
        for (T it : items) {
            arr.add(it.json());
        }
        return arr;
    }

    public static <T extends JsonBackedDto> List<T> fromJsonArray(JsonArray arr, Class<T> type) {
        if (arr == null) return Collections.emptyList();
        List<T> out = new ArrayList<>(arr.size());
        for (Iterator<Object> it = arr.iterator(); it.hasNext();) {
            Object next = it.next();
            if (!(next instanceof JsonObject)) continue;
            JsonObject jo = (JsonObject) next;
            try {
                T instance = type.getDeclaredConstructor().newInstance();
                instance.setJson(jo);
                out.add(instance);
            } catch (ReflectiveOperationException e) {
                LOG.error("Failed to instantiate {}", type.getName(), e);
            }
        }
        return out;
    }
}

Code: DTO implemanted on top of the base class

import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

import java.util.Collections;
import java.util.List;

public class CallNumbersDto extends JsonBackedDto {

    public CallNumbersDto() {
        super();
    }

    public CallNumbersDto(JsonObject source) {
        super(source);
    }

    // Field keys
    public static final String KEY_APP_ID = "appId";
    public static final String KEY_NUMBERS = "nums";
    public static final String KEY_PRODUCT_NAME = "productName";
    public static final String KEY_PRIVACY = "privacy";

    // Accessors expected by frameworks
    public String getAppId() {
        return json().getString(KEY_APP_ID);
    }

    public void setAppId(String value) {
        json().put(KEY_APP_ID, value);
    }

    public List<String> getNums() {
        JsonArray arr = json().getJsonArray(KEY_NUMBERS);
        return arr == null ? Collections.emptyList() : arr.getList();
    }

    public void setNums(List<String> values) {
        json().put(KEY_NUMBERS, values == null ? null : new JsonArray(values));
    }

    public String getProductName() {
        return json().getString(KEY_PRODUCT_NAME);
    }

    public void setProductName(String value) {
        json().put(KEY_PRODUCT_NAME, value);
    }

    public String getPrivacy() {
        return json().getString(KEY_PRIVACY);
    }

    public void setPrivacy(String value) {
        json().put(KEY_PRIVACY, value);
    }
}

Typical high-frequency conversions that are no longer needed

// Previous approach with repeated (de)serialization
// JSONObject jo = (JSONObject) JSONObject.toJSON(fooBean);
// FooBean fooBean = (FooBean) JSONObject.toBean(jo, FooBean.class);

// New approach keeps everything in Vert.x types
CallNumbersDto dto = new CallNumbersDto();
dto.setAppId("app-1");
dto.setNums(List.of("10086", "10010"));

deployOnEventBus(dto.json()); // send JsonObject directly

Behavioral notes:

  • All state lives in JsonObject; no duplication of fields.
  • Getters/setters merely proxy to the underlying JsonObject, preserving compatibility with frameworks expecting JavaBeans.
  • Date/time fields should be serialized using the base helpers to enforce cnosistent wire formats.
  • When exchanging collections, use JsonBackedDto.toJsonArray and JsonBackedDto.fromJsonArray to convert between List<T> and JsonArray without intermediate string JSON.
Tags: Javavert.x

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.