[jackson] custom field annotation completes serialization logic

background

SpringThe default JSONserialization tool uses the project address jackson:GitHubhttps://github.com/FasterXML/jackson

When we deal with front-end and back-end interface interactions, we may need to implement various personalized requirements. This article mainly introduces custom annotations, and then performs business logic processing on specific fields.

Introduction to the development environment of this article

development dependencies Version
Spring Boot 3.1.2
JDK 17

Create a new annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({
    
    ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JsonSerialize(using = PrintFieldJsonSerializer.class)
public @interface PrintField {
    
    

    Type rule() default Type.TYPE_1;

    enum Type {
    
    
        TYPE_1, TYPE_2
    }
}

Create a new JavaBean

Use custom annotations on fields

import lombok.Builder;
import lombok.Data;

/**
 * @author tangheng
 */
@Data
@Builder
public class DemoPerson {
    
    

    @PrintField(rule = PrintField.Type.TYPE_1)
    private String name;
    @PrintField(rule = PrintField.Type.TYPE_2)
    private String email;
}

Create a new JsonSerializer

for custom business logic

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;

@Slf4j
@RequiredArgsConstructor
public class PrintFieldJsonSerializer extends JsonSerializer<String> {
    
    

    private final PrintField.Type rule;

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    
    
        if(StringUtils.isBlank(value)) {
    
    
            return;
        }
        switch (rule) {
    
    
            case TYPE_1:
                log.info("hello, value: {}", value);
                break;
            default:
                log.info("rule: {}, value: {}", rule, value);
                break;
        }
        gen.writeString(value);
    }
}

Create a new AnnotationIntrospector

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.Annotated;

import java.lang.annotation.Annotation;

public class PrintFieldAnnotationIntrospector extends AnnotationIntrospector {
    
    

    @Override
    public Version version() {
    
    
        return Version.unknownVersion();
    }

    @Override
    public Object findSerializer(Annotated a) {
    
    
        PrintField ann = _findAnnotation(a, PrintField.class);
        if (ann != null) {
    
    
            return new PrintFieldJsonSerializer(ann.rule());
        }
        return null;
    }

    @Override
    public boolean isAnnotationBundle(Annotation ann) {
    
    
        if (PrintField.class.isAssignableFrom(ann.getClass())) {
    
    
            return true;
        }
        return false;
    }
}
  • This step is very critical. Use jackson's Introspector mechanism to play a link between the preceding and the following.
  • Determine that there are custom annotations on the field, and then use the custom JsonSerializer
  • This ties the whole thing together

unit test

class PrintFieldJsonSerializerTest extends JsonSpringbootTestBase {
    
    

    private ObjectMapper objectMapper = new ObjectMapper();

    @SneakyThrows
    @Test
    void serialize() {
    
    
        objectMapper.setAnnotationIntrospector(new PrintFieldAnnotationIntrospector());

        DemoPerson demoPerson = DemoPerson.builder()
                .name("zhangsan")
                .email("[email protected]")
                .build();

        String testResult = objectMapper.writeValueAsString(demoPerson);
        log.info("testResult: {}", testResult);
        assertTrue(StringUtils.isNotBlank(testResult));
    }
}

Screenshot of unit test results
insert image description here

Summarize

  • There are tens of millions of ways to realize a requirement. For a pursuing programmer, study the source code to find the most refined way to achieve the goal in the simplest way, which is better in terms of scalability and maintainability.
  • When there is a need, don't rush to write code, study the source code, which can help us achieve our goal gracefully
  • Maybe the time spent studying source code is much longer than writing code, but the harvest and sense of accomplishment are still very satisfying
  • Even if the requirements are finally realized with only a few lines of code, a good programmer is never measured by the number of lines of code
  • The deserialization of jackson can be realized by referring to the same principle

Guess you like

Origin blog.csdn.net/friendlytkyj/article/details/132244878