当前位置: 首页 > article >正文

Json工具(一)- Jackson(续)

5、Jackson常用注解

@JsonProperty
自定义属性在序列化和反序列化过程中所对应的JSON字段的名称,还可以指定是否序列化和反序列化。属性如下:
value:设置属性的名称。一般当JSON串中的key与对象中的属性名称不一致,比如分别采用了下划线命名与驼峰命名。
defaultValue:用于记录预期的默认值
required:定义在反序列化期间是否需要属性的值。
index:序列化时属性的顺序,默认为-1,越小越靠前。
access:更改序列化和反序列化中逻辑属性的可见性,A逻辑属性的可见性仅根据可见性和其他注释确定,access的默认值。
READ_ONLY 逻辑属性仅在序列化时可见(属性值参与序列化)、WRITE_ONLY 逻辑属性仅在反序列化时可见(属性值参与反序列化)、READ_WRITE 逻辑属性在序列化和反序列化时都可见
@JsonAlias
反序列化的时候可以让Bean的属性接收多个json字段的名称。
@JsonAutoDetect
控制序列化和反序列化过程中Java对象中访问修饰符的序列化策略。用于注解或者类上,默认为ANY。
@JsonIgnore
在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。一般标记在属性或者方法上,返回的json数据即不包含该属性。
@JsonIgnoreProperties
此注解是类注解,作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。
@JsonIgnoreType
该类作为别的类的属性时,该属性忽略序列化和反序列化。
@JsonInclude
是为实体类在接口序列化返回值时增加规则的注解
ALWAYS为默认值,表示全部序列化,即默认返回全部字段
NON_NULL表示值为null就不序列化,即值为null的字段不返回(当实例对象中有Optional或AtomicReferenceAtomicReferenceAtomicReference类型的成员变量时,如果Optional或AtomicReference引用的实例为null,用NON_NULL 不能使该字段不做序列化,此时应使用NON_ABSENT规则)
NON_ABSENT可在实例对象中有Optional或AtomicReference类型的成员变量时,如果Optional或AtomicReference引用的实例为null时,也可使该字段不做序列化,同时可以排除值为null的字段
NON_EMPTY排除字段值为null、空字符串、空集合、空数组、Optional类型引用为空,AtomicReference类型引用为空
NON_DEFAULT没有更改的字段不序列化,即未变动的字段不返回
CUSTOM:这个值要配合valueFilter属性一起使用,在序列化的时候会执行CustomFilter中的的equals方法,该方法的入参就是字段的值,如果equals方法返回true,字段就不会被序列化,如果equals方法返回false时字段才会被序列化,即true时不返回,false时才返回

@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = CustomFilter.class)
private String field;

static class CustomFilter {
    @Override
    public boolean equals(Object obj) {
        // 为null,或者不是字符串就返回true,即不返回该字段
        if (null == obj || !(obj instanceof String)) {
            return true;
        }

        // 长度大于2就返回true,意味着不被序列化
        return ((String) obj).length() > 2;
    }
}
@JsonFormat
是一个时间格式化注解,比如我们存储在mysql中的数据是date类型的,当我们读取出来封装在实体类中的时候,就会变成英文时间格式,而不是yyyy-MM-dd HH:mm:ss这样的中文时间,因此我们需要用到JsonFormat注解来格式化我们的时间。
@JsonUnwrapped
表明属性应该以扁平化的形式进行序列化,即目标属性将不会序列化为 JSON 对象,但其属性将序列化为包含它的对象的属性。
@JacksonInject
在使用JSON格式进行反序列化的时候,我们经常有这样一些需求。我们从客户端或者其他渠道获取了一个JSON格式的数据对象,该对象包含若干个属性。但是我们在将JSON字符串反序列化的时候,需要给它加上一些默认数据

@Data
public class PlayerStar {
    private String name;
    private Integer age;
    // 业余爱好,数组
    private String[] hobbies;
    // 朋友
    private List<String> friends;
    // 年收入 Map
    private Map<String, BigDecimal> salary;

    @JacksonInject("responseTime")
    private LocalDateTime responseTime;
}

public class App {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 为responseTime赋值为当前值
        InjectableValues.Std injectableValues = new InjectableValues.Std();
        injectableValues.addValue("responseTime", LocalDateTime.now());

        // 在反序列化过程中赋值给对象
        objectMapper.setInjectableValues(injectableValues);

        String jsonInString = "{\"name\":\"乔丹\",\"age\":45,\"hobbies\":[\"高尔夫球\",\"棒球\"]}";
        PlayerStar jordan = objectMapper.readValue(jsonInString, PlayerStar.class);

        System.out.println(jordan);
    }

}
@JsonAnyGetter和@JsonAnySetter
@JsonAnySetter:可以将序列化时未识别到的属性都扔进一个map里。
@JsonAnyGetter:将map里的属性都平铺输出。
public class TestAnyGetterAndSetter {
    public static void main(String[] args) throws JsonProcessingException {
        String json1 = "{\"name\":\"zhangSan\",\"id\":1,\"address\":\"china\"}";
        ObjectMapper objectMapper = new ObjectMapper();
        Account account = objectMapper.readValue(json1, Account.class);
        System.out.println(account.getMap());
        String jsonString = objectMapper.writeValueAsString(account);
        System.out.println(jsonString);
    }


}

class Account {

    private long id;
    private String name;
    private Map<String, Object> map;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @JsonAnySetter
    public void setMap(String key, Object value) {
        if (map == null) {
            map = new HashMap<>();
        }
        map.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, Object> getMap() {
        return map;
    }
}
@JsonSetter 和 @JsonGetter
@JsonSetter 和 @JsonGetter:@JsonSetter只能用于setter方法,@JsonGetter只能用户getter方法,只用二者中的一个标签时,与@JsonProperty作用一模一样。
@JsonCreator
用于构造方法或者静态方法上面,加了@JsonCreator注解,该类的对象在反序列化时,就走有@JsonCreator注解的方法来反序列化,原先无参+set的过程失效。
public class TestJsonCreator {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Page page = objectMapper.readValue("{" +
                "    \"page_size\":12,\n" +
                "    \"page_num\":1,\n" +
                "    \"name\":\"Java\"\n" +
                "}", Page.class);
        System.out.println(page);
    }
}

@Data
@AllArgsConstructor
class Page {
    @JsonProperty(value = "page_size")
    private int pageSize;

    @JsonProperty(value = "page_num")
    private int pageNum;

    private String name;


    @JsonCreator
    public static Page unSerialize() {
        System.out.println("正在反序列化成对象");
        return new Page(12, 1, "Java");
    }
}
public class TestJsonCreator {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Page page = objectMapper.readValue("{" +
                "    \"page_size\":12,\n" +
                "    \"page_num\":1,\n" +
                "    \"name\":\"Java\"\n" +
                "}", Page.class);
        System.out.println(page);
    }
}

@Data
class Page {
    @JsonProperty(value = "page_size")
    private int pageSize;

    @JsonProperty(value = "page_num")
    private int pageNum;

    private String name;

    @JsonCreator
    public Page(@JsonProperty("name") String name,
                @JsonProperty("page_num") int pageNum,
                @JsonProperty("page_size") int pageSize) {
        System.out.println("@JsonCreator生效");
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.name = name;
    }
}
@JsonValue
用于get方法、属性字段,一个类只能用一个,加上这个注解时,该类的对象序列化时就会只返回这个字段的值做为序列化的结果。比如一个枚举类的get方法上加上该注解,那么在序列化这个枚举类的对象时,返回的就是枚举对象的这个属性,而不是这个枚举对象的序列化json串。
@JsonPropertyOrder
用于指定实体生成 json 时的属性顺序,一般用得不多
@JsonRawValue
能够按原样序列化属性。属性值不会被转义或者加引号(或者说,会自动去掉转义,多余的引号)。属性值已经是一个 JSON String,或者属性值已经被加了引号时很有用。
public class TestJsonRawValue {
    public static void main(String[] args) throws JsonProcessingException {
        News news = new News();
        news.setContent("\"中国No.1\"");
        news.setTitle("中国崛起!");
        news.setPublishTime(new Date());
        System.out.println("-- before serialization --");
        ObjectMapper objectMapper = new ObjectMapper();

        String jsonString = objectMapper.writeValueAsString(news);
        System.out.println(jsonString);

        News news1 = objectMapper.readValue(jsonString, News.class);
        System.out.println(news1);
    }
}

@Data
class News {
    @JsonRawValue
    private String content;
    private String title;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date publishTime;
}

-- before serialization --
{"content":"\"中国No.1\"","title":"中国崛起!","publishTime":"2024-02-28 10:47:00"}
News(content="中国No.1", title=中国崛起!, publishTime=Wed Feb 28 10:47:00 CST 2024)

-- after serialization --
{"content":"中国No.1","title":"中国崛起!","publishTime":"2024-02-28 10:47:41"}
News(content=中国No.1, title=中国崛起!, publishTime=Wed Feb 28 10:47:41 CST 2024)

@JsonRootName
用来指定root wrapper的名字。注意,只有当WRAP_ROOT_VALUE开启时,此注解才生效
@JsonSerialize
用于字段或get方法上,自定义序列化过程
@JsonDeserialize
用于字段或set方法上,自定义反序列化过程
@JacksonAnnotation
标注该注解是Jackson的注解,会被Jackson处理
@JacksonAnnotationsInside
自定义Jackson注解的组合注解
@JsonView
不同请求获取的对象的视图不同(属性数量不一致),使用@JsonView配合自定义视图实现
public class CompanyViews {

    public static class Normal {
    }

    ;

    public static class Manager extends Normal {
    }

    ;

    public static class HR extends Normal {
    }

    ;

}
public class Staff {
    @JsonView(CompanyViews.Normal.class)
    private String name;

    @JsonView(CompanyViews.Normal.class)
    private int age;

    // two views
    @JsonView({CompanyViews.HR.class, CompanyViews.Manager.class})
    private String[] position;

    @JsonView(CompanyViews.Manager.class)
    private List<String> skills;

    @JsonView(CompanyViews.HR.class)
    private Map<String, BigDecimal> salary;
}    
public class JacksonExample {

    public static void main(String[] args) {

        ObjectMapper mapper = new ObjectMapper();

        Staff staff = createStaff();

        try {

            mapper.enable(SerializationFeature.INDENT_OUTPUT);

            // normal
            String normalView = mapper.writerWithView(CompanyViews.Normal.class).writeValueAsString(staff);

            System.out.format("Normal views\n%s\n", normalView);

            // manager
            String managerView = mapper.writerWithView(CompanyViews.Manager.class).writeValueAsString(staff);

            System.out.format("Manager views\n%s\n", managerView);

            // hr
            String hrView = mapper.writerWithView(CompanyViews.HR.class).writeValueAsString(staff);

            System.out.format("HR views\n%s\n", hrView);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private static Staff createStaff() {

        Staff staff = new Staff();

        staff.setName("mkyong");
        staff.setAge(38);
        staff.setPosition(new String[]{"Founder", "CTO", "Writer"});
        Map<String, BigDecimal> salary = new HashMap() {{
            put("2010", new BigDecimal(10000));
            put("2012", new BigDecimal(12000));
            put("2018", new BigDecimal(14000));
        }};
        staff.setSalary(salary);
        staff.setSkills(Arrays.asList("java", "python", "node", "kotlin"));

        return staff;

    }

}
Normal views
{
  "name" : "mkyong",
  "age" : 38
}
 
Manager views
{
  "name" : "mkyong",
  "age" : 38,
  "position" : [ "Founder", "CTO", "Writer" ],
  "skills" : [ "java", "python", "node", "kotlin" ]
}
 
HR views
{
  "name" : "mkyong",
  "age" : 38,
  "position" : [ "Founder", "CTO", "Writer" ],
  "salary" : {
    "2018" : 14000,
    "2012" : 12000,
    "2010" : 10000
  }
}

6、自定义序列化和反序列化案例

自定义序列化工具的步骤

  1. 实现JsonSerializer自定义序列化逻辑
  2. 实现JsonDeserializer自定义反序列化逻辑
    如需获取字段相关的其他元素信息需要实现ContextualDeserializer
    接口获取上下文。然后获取字段的JavaType,Jackson提供了TypeFactory获取JavaType,调用ObjectCodec工具反序列化
    没有泛型无需获取JavaType,直接使用readValue(String content, Class valueType)
  3. 在需要处理的字段添加注解
/*案例相关entity*/
@Data
public class MyDto {
    @ObjAndArrayConverter(clazz = Bodys.class, internal = Person.class)
    @JsonProperty("bodys")
    private Bodys<Person> bodys;
}

@Data
public class Bodys<T> {

    @JsonProperty("commonField")
    private String commonField;

    private T body;
}

@Data
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.ANY)
public class Person {
    @JsonProperty(value = "myName", index = 4)
    private String name;
    @JsonProperty(value = "myAge", index = 3)
    private Integer age;
    @JsonProperty(value = "birthday", index = 2)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Date date;
    @JsonProperty(value = "myHeight", index = 1)
    private int height;
    @JsonIgnore
    private Action action;
    @JacksonInject(value = "responseTime")
    private Date responseTime;
}
/*自定义序列化工具*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonDeserialize(using = ObjAndArrayDeserializer.class)
@JsonSerialize(using = ObjAndArraySerializer.class)
public @interface ObjAndArrayConverter {
    Class<?> clazz();

    Class<?> internal();
}

/*自定义序列化处理逻辑*/
public class ObjAndArraySerializer extends JsonSerializer {
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartArray();
        gen.writeObject(value);
        gen.writeEndArray();
    }
}

/*自定义反序列化处理逻辑*/
public class ObjAndArrayDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {

    private Class<?> clazz;

    private Class<?> internal;

    /**
     * jackson 反序列化执行的时候需要知道当前的字段需要反序列化到哪个对象,需要获取对应的JavaType,如果没有泛型可以不获取JavaType。
     * 如果是Person的JavaType TypeFactory.defaultInstance().constructType(Person.class);可以不获取JavaType,直接使用readValue(String content, Class<T> valueType)
     */
    private JavaType javaType;

    public ObjAndArrayDeserializer(Class<?> clazz, Class<?> internal) {
        super(clazz);
        this.clazz = clazz;
        this.internal = internal;
        // 获取JavaType的工厂对象以获取JavaType
        TypeFactory typeFactory = TypeFactory.defaultInstance();
        // 获取Bodys<Person>对应的JavaType
        this.javaType = typeFactory.constructParametricType(this.clazz, this.internal);
        // 如果是List<Person>对应的JavaType,TypeFactory.defaultInstance().constructParametricType(List.class,Person.class);

    }

    public ObjAndArrayDeserializer() {
        super(Object.class);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        JsonToken currentToken = p.getCurrentToken();
        if (currentToken == JsonToken.START_ARRAY) {
            p.nextToken();
            Object o = p.getCodec().readValue(p, this.javaType);
            p.nextToken();
            return o;
        } else {
            return null;
        }
    }

    /*获取Field的相关信息,以获取对应的JavaType,缓存了反序列化器,需要重启才会再走*/
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        ObjAndArrayConverter converter = property.getAnnotation(ObjAndArrayConverter.class);
        this.clazz = converter.clazz();
        this.internal = converter.internal();
        return new ObjAndArrayDeserializer(converter.clazz(), converter.internal());
    }
}

7、Jackson多态的序列化与反序列化


@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "type",
        visible = true
)
@JsonSubTypes(
        value = {
                @JsonSubTypes.Type(value = JacksonProduct1.class, name = "product1"),
                @JsonSubTypes.Type(value = JacksonProduct2.class, name = "product2")
        }
)
@Data
public abstract class JacksonParent {
    /*抽象的公有字段*/
    private String year = "2024";
}

@Data
public class JacksonProduct1 extends JacksonParent {
    private String type = "product1";
    private String name;
}

@Data
public class JacksonProduct2 extends JacksonParent {
    private String type = "product2";
    private String name;
}

@Data
public class JacksonProduct {
    private List<JacksonParent> jacksonProduct;
}

public static void main(String[] args) throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    JacksonProduct jacksonProduct = new JacksonProduct();
    JacksonProduct1 jacksonProduct1 = new JacksonProduct1();
    jacksonProduct1.setName("json1");
    JacksonProduct2 jacksonProduct2 = new JacksonProduct2();
    jacksonProduct2.setName("json2");
    List<JacksonParent> list = new ArrayList<>();
    list.add(jacksonProduct1);
    list.add(jacksonProduct2);
    jacksonProduct.setJacksonProduct(list);
    String s = objectMapper.writeValueAsString(jacksonProduct);
    System.out.println("序列化成字符串:" + s);
    // 序列化成字符串:{"jacksonProduct":[{"year":"2024","type":"product1","name":"json1"},{"year":"2024","type":"product2","name":"json2"}]}
    System.out.println("转换后的实体类结果:");
    System.out.println(objectMapper.readValue(s, JacksonProduct.class));
    // JacksonProduct(jacksonProduct=[JacksonProduct1(type=product1, name=json1), JacksonProduct2(type=product2, name=json2)])
}

8、Jackson序列化和反序列化执行流程

Jackson序列化流程

1. 获取相关的ObjectCodec
2. 初始化JsonGenerator
3. 获取DefaultSerializerProvider
4. 获取JsonSerializer
5. 开始序列化
6. 关闭资源

Jackson反序列化流程

1. 获取相关的ObjectCodec
2. 初始化JsonParser
3. DefaultDeserializationContext
4. 初始化JsonToken
5. 获取JsonDeserializer
6. 开始反序列化
7. 关闭资源

http://www.kler.cn/a/576067.html

相关文章:

  • 网络安全审计 主要包括哪些内容
  • 盛铂科技 SLMF315频率综合器200MHz至15GHz 国产频综模块
  • 锂电池寿命预测 | Matlab基于ARIMA的锂电池寿命预测
  • DeepSeek + Kimi:高效制作PPT实战详解
  • AI,重新定义阿里云
  • 《打造视频同步字幕播放网页:从0到1的技术指南》
  • 计算机毕业设计SpringBoot+Vue.js众筹网站系统(源码+文档+PPT+讲解)
  • 百度SEO关键词布局从堆砌到场景化的转型指南
  • STM32-I2C通信协议
  • C# 命名空间(Namespace)详解
  • 椭圆曲线密码学数学证明推导及实践:基于Python实现与GPU加速GUI演示
  • 视觉-语言模型-出发点CLIP--(挖掘源码-网络框架)
  • 项目设计之用户注册与登录
  • 基于字符的卷积网络在文本分类中的应用与探索
  • 正十七边形尺规作图证明——从高斯的发现到几何实现
  • 记录一次mysql全文索引不生效
  • 六十天前端强化训练之第十一天之事件机制超详解析
  • Windows系统中在VSCode上配置CUDA环境
  • hadoop框架与核心组件刨析(二)HDFS
  • 网络安全需要报班学习吗?