Java:序列化和反序列化

1.概念与作用

1.1概念

序列化:对象转换为字节序列的过程,本质上是将一个对象转换成二进制的byte[]数组 反序列化:字节序列恢复为对象的过程

两种作用:

  • 将对象序列化后永久的保存到硬盘中,通常是存在一个文件中;
  • 在网络通信传递数据时需要;网络之间的通信均是使用二进制的形式互相通信,无论是发送还是接受,因为序列化和反序列化对网络通信至关重要。

2.对象序列化和反序列化

2.1JDK的序列化API

  1. java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  2. java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。 注意:

    前提是实现Serializable接口; transient关键字修饰的字段不进行序列化; 静态字段不会序列化; Externalizable接口使用实现自定序列化;

    2.2使用Jackson序列化

    Jackson是SpringBoot自带的Json数据处理依赖包,但是其不依赖于Spring的库。

1. @JsonProperty:用于改变某个字段序列化的名;
  public class Human{

     @JsonProperty("first_name")
      private String firstName;

  }
2. @JsonInclude: 将某个为null的字段不显示在序列化结果中;
  全局
   ObjectMapper mapper = new ObjectMapper();
   mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

   某个字段
   @JsonInclude(JsonInclude.Include.NON_NULL)
   private String name;
3. @JsonIgnore 和 @JsonIgnoreProperties 忽略某个字段,不对其进行序列化操作;
  @JsonIgnoreProperties({"age"})
   public class Human{

      @JsonIgnore
      private String firstName;

      private int age;
  }
4. @JsonAlias 使用别名
     public class Human{

      @JsonAlias({"first_name, name, firstName"})
      private String firstName;

      private int age;
  }

在反序列化时,json字符串中有first_name, name 或者是 firstName均可以正确序列化成对象的字段firstName

2.3使用Jackson自定义序列化

序列化的过程通过get方法获取对象的属性数据,反序列化过程是先调用目标对象的无参构造函数生成一个对象,再通过对象的set方法为对象中的每一个属性进行赋值。因此通常一个对象想要序列化/反序列化则必须有get, set 和public的无参构造函数(java中不声明默认会有),也可以使用自定义的构造函数来进行反序列化。

1. 使用 @JsonCreator

public class CustomSerialization {
    public static void main(String[] args) throws IOException {
        Human human = new Human("zhangsan", 1, Lists.newArrayList("eat", "sleep"));
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = objectMapper.writeValueAsString(human);
        System.out.println(jsonString);
        Human humanFromSer = objectMapper.readValue(jsonString, Human.class);
        System.out.println(humanFromSer);
    }
    public static class Human{

        private String name;

        private int sex;

        private List<String> hobbies;

        @JsonCreator
        private Human(@JsonProperty("name") String name, @JsonProperty("sex") int sex, @JsonProperty("hobbies") List<String> hobbies){
            this.name = name;
            this.sex = sex;
            this.hobbies = hobbies;
        }
        public String getName() {
            return name;
        }
        public int getSex() {
            return sex;
        }
        public List<String> getHobbies() {
            return hobbies;
        }
        public void setName(String name) {
            this.name = name;
        }
        public void setSex(int sex) {
            this.sex = sex;
        }
        public void setHobbies(List<String> hobbies) {
            this.hobbies = hobbies;
        }
    }
}

Note:使用@JsonCreator时,其构造函数必须有@JsonProperty作为转换

2. 使用 @ConstructorProperties

@JsonCreator@ConstructorProperties 作用使一致的,后者只能加在构造方法上,作为反序列化函数,后者使用起来比前者方便。不需要在构造函数中每个属性名前加@JsonProperty注解。

    @ConstructorProperties({"name", "sex", "hobbies"})
        private Human( String name,  int sex,  List<String> hobbies){
            this.name = name;
            this.sex = sex;
            this.hobbies = hobbies;
        }

Note: 该注解中的属性名称必须和构造函数中的属性名称保持一致,不然会解析失败报错

2.4 自定义序列化及反序列化类型转换器

1. 继承StdConverter类

定义了一个Money类, 该类中定义一个final的字段, 这是为了不允许Json反序列化时调用set方法

public class CustomSerialization {
    public static void main(String[] args) throws IOException {
        Human human = new Human();
        human.setName("zhangdan");
        human.setSex(1);
        human.setHobbies(Lists.newArrayList("eat", "sleep"));
        Money money = new Money("USD");
        human.setMonies(Lists.newArrayList(money));
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = objectMapper.writeValueAsString(human);
        System.out.println(jsonString);
        Human humanFromSer = objectMapper.readValue(jsonString, Human.class);
        System.out.println(humanFromSer);
    }
    public static class Human{

        private String name;

        private int sex;

        private List<String> hobbies;

        @JsonSerialize(converter = MoneyConverter.class)
        @JsonDeserialize(converter = MoneyDeserialize.class)
        private List<Money> money;

        public String getName() {
            return name;
        }
        public int getSex() {
            return sex;
        }
        public List<String> getHobbies() {
            return hobbies;
        }
        public void setName(String name) {
            this.name = name;
        }
        public void setSex(int sex) {
            this.sex = sex;
        }
        public void setHobbies(List<String> hobbies) {
            this.hobbies = hobbies;
        }
        public List<Money> getMoney() {
            return money;
        }

        public void setMonies(List<Money> money) {
            this.money = money;
        }
    }
    public static class Money{
        private final String type;

        public Money(String type) {
            this.type = type;
        }
        public String getType() {
            return type;
        }
    }
}

public class MoneyConverter extends StdConverter<List<CustomSerialization.Money>, List<String>> {
    @Override
    public List<String> convert(List<CustomSerialization.Money> money) {
        return money.stream().map(CustomSerialization.Money::getType).collect(Collectors.toList());
    }
}

public class MoneyDeserialize extends StdConverter<List<String>, List<CustomSerialization.Money>> {
    @Override
    public List<CustomSerialization.Money> convert(List<String> strings) {
        return  strings.stream().map(str ->new CustomSerialization.Money(str)).collect(Collectors.toList());
    }
}

Note: 使用继承StdConverter的方式时, @JsonSerialize使用converter方法

2. 继承JsonSerializer和JsonDeserializer类
@Data
public class Person {
    private int age;
    private String address;
    private Man man;
    private List<Man> men;
    @Data
    public static class Man{
        @JsonSerialize(using = TimeSerializer.class)
        @JsonDeserialize(using = TimeDeserializer.class)
        private Instant time;
        private Woman wife;
    }
    @Data
    public static class Woman{
        private String style;
        private int beforeBoyFriends;
    }
    public static void main(String[] args) throws IOException {
        Person person = new Person();
        person.setAge(30);
        person.setAddress("NewYork");
        Woman woman = new Woman();
        woman.setStyle("style");
        woman.setBeforeBoyFriends(3);
        Man man1 = new Man();
        man1.setWife(woman);
        man1.setTime(Instant.now());
        person.setMan(man1);

        Man man2 = new Man();
        man2.setWife(woman);
        man2.setTime(Instant.now());
        person.setMen(Lists.newArrayList(man1, man2));

        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = objectMapper.writeValueAsString(person);
        System.out.println(jsonString);
        Person personDemo= objectMapper.readValue(jsonString, Person.class);
        System.out.println(personDemo.toString());
    }
}

总结:这种自定义序列化的方式只支持某个字段的。 如果某个List存储的是一个多属性的对象,并且你想对该对象进行选择性或自定义序列化,此时你只能对该对象中的字段进行操作,而不能对其整个List对象进行操作。如果有大佬知道通知一下小编。