Solve Long Precision Loss in Spring Boot 3 with Jackson and Custom Annotations

This article demonstrates how to handle Java Long precision loss in Spring Boot 3 front‑end displays, configures Jackson to serialize Long as String, uses @JsonValue for single‑property serialization, and creates custom annotations with a SensitiveSerializer for data masking, complete with code examples and screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Solve Long Precision Loss in Spring Boot 3 with Jackson and Custom Annotations

In previous articles we introduced extensive Jackson usage in Spring Boot. This article focuses on two main topics: Long precision loss in the front‑end and @JsonValue serialization of single property values, plus the creation of custom annotations for sensitive data masking.

1. Introduction

The following topics are covered:

Long type precision loss in the front‑end

@JsonValue serialization of single property values

Custom annotation application

2. Practical Cases

2.1 Long Precision Loss

When JavaScript handles large numbers such as Java Long, precision can be lost because JavaScript uses IEEE‑754 64‑bit floating‑point numbers, which cannot accurately represent integers larger than 2^53‑1. The following controller returns a 17‑digit Long value:

@RestController
@RequestMapping("/longs")
public class LongController {

  @GetMapping("")
  public Map<String, Object> getData() {
    return Map.of("code", 0, "data", 123456789012345678L);
  }
}

Directly accessing the endpoint in a browser shows the correct value, but the network response contains the truncated number. An Ajax request also returns the incorrect value:

function getData() {
  axios.get('http://localhost:8080/longs')
    .then(resp => {
      console.log(resp.data);
    }).catch(error => {
      console.log(error);
    });
}

To prevent this issue, configure Jackson to serialize all Long values as String globally:

@Component
public class PackMapperCustomizer implements Jackson2ObjectMapperBuilderCustomizer {

  @Override
  public void customize(Jackson2ObjectMapperBuilder builder) {
    builder.serializerByType(Long.class, ToStringSerializer.instance);
  }
}

If only specific fields need conversion, annotate them with @JsonSerialize(using = ToStringSerializer.class):

public class User {

  @JsonSerialize(using = ToStringSerializer.class)
  private Long id;
  private String name;
  // getters, setters
}

2.2 @JsonValue Serialization of Single Property Values

The @JsonValue annotation marks a method or field whose value will be used as the JSON representation of the entire object. For example, an enum:

public enum PaymentStatus {
  NO_PAY(0, "未支付"), PAID(1, "已支付");

  private Integer code;
  private String desc;
  // constructor, getters, setters
}

When the enum is returned from a controller, the default JSON output is the enum name. Adding @JsonValue to a field makes that field the JSON value:

@JsonValue
private String desc;

Applying @JsonValue to a regular bean field yields the same effect:

public class User {

  @JsonSerialize(using = ToStringSerializer.class)
  private Long id;

  @JsonValue
  private String name;
}

2.3 Custom Annotation for Sensitive Data Masking

To mask sensitive fields such as ID cards or phone numbers, define a custom annotation using @JacksonAnnotationsInside and a serializer:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerializer.class)
public @interface Sensitive {
  int start() default 0;
  int end() default 0;
  String mask() default "*";
}

The corresponding serializer implements the masking logic:

public class SensitiveSerializer extends JsonSerializer<String> implements ContextualSerializer {

  private Sensitive sensitive;

  @Override
  public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    String val = value;
    if (sensitive != null && StringUtils.hasLength(val)) {
      String m = sensitive.mask();
      int start = sensitive.start();
      int end = sensitive.end();
      int totalLength = value.length();
      // masking algorithm omitted for brevity
    }
    gen.writeString(val);
  }

  @Override
  public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
    sensitive = property.getAnnotation(Sensitive.class);
    return this;
  }
}

Use the annotation on fields that require masking:

public class User {

  @JsonSerialize(using = ToStringSerializer.class)
  private Long id;
  private String name;

  @Sensitive(start = 6, end = 4)
  private String idCard;

  @Sensitive(start = 4, end = 3)
  private String phone;
  // getters, setters
}

The front‑end now displays masked values for the annotated fields.

Overall, the article provides practical solutions for Long precision handling, single‑property JSON serialization, and custom data masking in Spring Boot 3 applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootCustom AnnotationJacksondata maskingjson serializationLong precision
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.