Jackson反序列化(学习笔记1)
2023-12-18 00:4:31 Author: 白帽子(查看原文) 阅读量:13 收藏

Jackson的大致了解

Jackson的简介

Jackson是一个开源的Java序列化和反序列化工具,可以将Java对象序列化为XML或JSON格式的字符串,以及将XML或JSON格式的字符串反序列化为Java对象。由于其使用简单,速度较快,且不依靠除JDK外的其他库,被众多用户所使用。

从这句话中可以得知Javason和fastJson以及yaml是差不多的,都是序列化和反序列化的。

jackson依赖
<dependency>   <groupId>com.fasterxml.jackson.core</groupId>   <artifactId>jackson-databind</artifactId>   <version>2.7.9</version></dependency><dependency>   <groupId>com.fasterxml.jackson.core</groupId>   <artifactId>jackson-core</artifactId>   <version>2.7.9</version></dependency><dependency>   <groupId>com.fasterxml.jackson.core</groupId>   <artifactId>jackson-annotations</artifactId>   <version>2.7.9</version></dependency>

Javason为我们提供了两个方法,一个是序列化的方法,一个是反序列化的方法。

writeValueAsString方法(序列化方法)

创建JavaBean:

package com.powernode;
public class User { private String name; private int age;
public User() { }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; }}

测试类:

package com.powernode;


import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonPoc { public static void main(String[] args) throws Exception { User user = new User(); user.setAge(18); user.setName("张三"); ObjectMapper objectMapper = new ObjectMapper(); System.out.println(objectMapper.writeValueAsString(user)); }}

输出:

readValue(反序列化方法)

JacksonPolymorphicDeserialization机制

在fastjson和yaml中都有指定我们反序列化的类型,比如fastjson是使用@type来指定的,Yaml中是使用tag或者!!来指定的,那么jackson中是不是也有指定类型的呢?

比如子类继承问题,比如反序列化的时候是否需要反序列化子类 ?反序列化哪个子类?

在jackson中实现了JacksonPolymorphicDeserialization机制,在反序列化的过程中,如果类的成员变量不是具体类型的话(Object,接口等),我们可以在JSON字符串中指定具体类型,Jackson将生成具体类型的实例。

有两种方式可以实现:

DefaultTyping

DefaultTyping接口包含四个值。下面一一来介绍这四个值。

JAVA_LANG_OBJECT

通过注解我们得知此值意味着当我们的类中如果有Object类型的属性的时候,他就会进行序列化和反序列化(这个值必须是一个可以被序列化和反序列化的类),我们可以通过enableDefaultTyping方法来设置。在他反序列化的时候他会调用无参的构造方法,所以再给他属性赋值的时候,如果选择使用构造器器赋值的话,一定要给他添加一个无参的构造器,如果没有的话,会报错。

可以看到这里已经序列化和反序列化成功了,也就是说,如果类中有Object属性的话,并且这个类是可以被序列化和反序列化的,那么他就会连带这个类一起还原出来。

举例:

package com.powernode;
import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;
import javax.swing.*;import java.io.Serializable;
public class ceshi1 { public static void main(String[] args) throws Exception { student1 student = new student1("张三",18,new s()); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT); String json = mapper.writeValueAsString(student); System.out.println(json); student1 student1 = mapper.readValue(json, student1.class); System.out.println(student1);
}}
class student1 {
private String name; private int age ;
private Object object;
public student1(){
}

public student1(String name, int age, Object object) { this.name = name; this.age = age; this.object = object; }
public Object getObject() { return object; }
public void setObject(Object object) { this.object = object; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "student1{" + "name='" + name + '\'' + ", age=" + age + ", object=" + object + '}'; }}
class s{
private static final long serialVersionUID = 669538327736493L; public int length = 100;}

OBJECT_AND_NON_CONCRETE

这个值表示如果有接口,或者抽象类的时候,他会进行序列化和反序列化。

举例:

package com.powernode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ceshi1 { public static void main(String[] args) throws Exception { student1 student = new student1("张三", 18,new myDog()); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); String json = mapper.writeValueAsString(student); System.out.println(json); student1 student1 = mapper.readValue(json, student1.class); System.out.println(student1);
}}
class student1 {
private String name; private int age ;
private Dog dog;
public student1(){
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Dog getDog() { return dog; }
// public void setDog(Dog dog) {// this.dog = dog;// }
public student1(String name, int age, Dog dog) { this.name = name; this.age = age; this.dog = dog; }
@Override public String toString() { return "student1{" + "name='" + name + '\'' + ", age=" + age + ", dog=" + dog + '}'; }}

class myDog implements Dog{ public String name = "relay"; @Override public void hi() { System.out.println("123"); }}
interface Dog{ public void hi();}

###### NON_CONCRETE_AND_ARRAYS

这个值代表可以序列化和反序列化数组类型。

举例:

package com.powernode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ceshi1 { public static void main(String[] args) throws Exception { Cat[] cats = new Cat[2]; cats[0] = new Cat(); cats[1] = new Cat(); student1 student = new student1("张三", 18,new myDog(),cats); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); String json = mapper.writeValueAsString(student); System.out.println(json); student1 student1 = mapper.readValue(json, student1.class); System.out.println(student1);
}}
class student1 {
private String name; private int age ;
private Dog dog;
private Object object;
public student1(){
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Dog getDog() { return dog; }
// public void setDog(Dog dog) {// this.dog = dog;// }

public void setDog(Dog dog) { this.dog = dog; }
public Object getObject() { return object; }
public void setObject(Object object) { this.object = object; }
public student1(String name, int age, Dog dog, Object object) { this.name = name; this.age = age; this.dog = dog; this.object = object; }
@Override public String toString() { return "student1{" + "name='" + name + '\'' + ", age=" + age + ", dog=" + dog + ", object=" + object + '}'; }}

class myDog implements Dog{ public String name = "relay"; @Override public void hi() { System.out.println("123"); }}
interface Dog{ public void hi();}class Cat{ private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }}

NON_FINAL

这个值表示除了final外的属性信息都需要被序列化和反序列化。

从这几个值来看,这几个值是慢慢放大范围的。

DefaultTyping类型描述说明
JAVA_LANG_OBJECT属性的类型为Object
OBJECT_AND_NON_CONCRETE属性的类型为Object、Interface、AbstractClass
NON_CONCRETE_AND_ARRAYS属性的类型为Object、Interface、AbstractClass、Array
NON_FINAL所有除了声明为final之外的属性
@JsonTypeInfo注解

这个注解是为了给属性加标识,就跟@Bean 或者给类上面加的@Controller差不多。

JsonTypeInfo.Id.NONE

使用这个注解标识属性和没有设置是一样的。

JsonTypeInfo.Id.CLASS

这个值在序列化的时候object属性中多了一个@Class的属性,值为我们的全限定类名。那是不是可以在反序列化的时候,可以通过@class的方式来指定相关的类名字。进行调用。如下图:

JsonTypeInfo.Id.MINIMAL_CLASS

这个属性值和上面那个其实是差不多的,上面多了一个@class的属性,这个多了一个@c的属性,同样也可以在反序列化的时候通过@c来指定相关的类的名称。

JsonTypeInfo.Id.NAME

可以看到报错了,这个值表示我们在序列化的时候他并没有指定全限定类名,而是多了一个@type,在反序列化的时候他找不到类就报错了。

JsonTypeInfo.Id.CUSTOM

这个值是给用户自定义的,需要手写解析器。

小总结

到这里我们发现注解值为JsonTypeInfo.Id.MINIMAL_CLASS 和 JsonTypeInfo.Id.CLASS以及使用ObjectMapper.enableDefaultTyping()方法赋值的都是可以反序列化利用的

流程分析

在调试之前呢我们看一下在他以及子类序列化的时候调用了那些方法,比如get,set,构造器这些。

序列化时

我们发现在序列化时,他调用了所有的属性的get方法,以及子类的构造器

反序列化时

在反序列化的时候它调用了所有属性的set方法以及无参构造方法。

流程分析

我们在readValue反序列化方法这里下断点跟进去。

首先我们的content参数就是我们的JSON字符串,通过调用createParser方法进行封装。我们跟进去。

来到createParser方法,首先获取我们json字符串的长度,然后然后进行判断,这里是不成立的,然后调用_createContext方法进行封装。

可以发现他将我们的字符串放到了IOContext类的_sourceRef属性中,最后返回。

然后调用_readMapAndClose方法,我们跟进去。来到deserialize方法,继续跟进去。

来到vanillaDeserialize方法。首先调用createUsingDefault创建一个目标类无参的构造方法,也就是我们的student1这个类,我们跟进去。

来到createUsingDefault方法,跟进call方法。这里就直接将我们的对象创建出来了。

回到vanillaDeserialize方法,接着调用getCurrentName获取student1类的属性名。

然后调用find方法获取到属性的方法名,然后调用deserializeAndSet方法,继续跟进deserialize方法。

来到deserialize方法,这里通过_valueTypeDeserializer来判断如果这个属性是带有类型的话,就使用deserializeWithType方法来反序列化,如果不带的话,那么就使用deserialize来反序列化。我们此时的类型是系统的类型就是String的类型,所以进入deserialize方法,我们跟进去。

来到deserialize方法,这里直接返回对应的值。

返回到deserializeAndSet方法,调用invoke方法,也就是调用setName方法,将我们的无参构造器以及刚拿到的值传递进去。

跟进去回来到setName方法。

剩下那几个也是一样的,我们主要看Object类型他是怎么处理的。

来到deserialize方法,因为我们是Object类型的所以跟进deserializeWithType方法。

首先判断我们的tokenid,tokenid位3所以进入到deserializeTypedFromAny方法

来到deserializeTypedFromAny方法,跟进_deserialize方法。

来到deserialize方法,首先调用locateTypeId方法获取到我们的类名,然后调用_findDeserializer方法获取到反序列化器,我们跟进去。

来到_findDeserializer方法,首先在map中查找我们的全限定类名,此时的map是空的,所以是为null,进入if判断,然后调用typeFromId方法进入该方法。

来到typeFromId方法,通过findClass方法查找到全限定类名。然后返回。

回到_deserialize方法,然后进入到deserialize方法,有没有发现这一幕很熟悉?没错跟我们上面去解析的那个属性是一样的,我们跟进vanillaDeserialize方法。

来到vanillaDeserialize方法,此时他实例化的无参构造方法就是我们Dog类的了。刚开始实例化的是我们的student1,接下来的操作就跟我们前面是一样的了。

最终还是会调用我们的setObject方法。

流程就到此结束了。

参考:

https://fynch3r.github.io/%E3%80%90%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E3%80%91Jackson/

如果有哪里不对的地方,请师傅们指出谢谢师傅们:Get__Post


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650247221&idx=1&sn=a8a0b495efe527b76eada3e533e697ef&chksm=83f25931b67bb1533930fbeace02e89659f57769d1eb1c9318178d30a643c2cbee3fd66ed4b2&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh