Jackson使用解析

Jackson简介

Spring在@RequestBody@ResponseBody中对对象的反序列化和序列化,都是借助Jackson来实现的,如果想改变Spring序列化或者反序列化的规则,可以通过调整Spring内部ObjectMapperbean的属性来实现:

@Configuration
public class JacksonConfig{
    @Bean
    public ObjectMapper getObjectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        //该例子将实体类的Date类型跟指定格式的日期字符串对应
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        return objectMapper; 
    }
}

Jackson ObjectMapper的一般使用

public void test(){
    User user = new User();
    ...
    ObjectMapper mapper = new ObjectMapper();
    //序列化
    String json = mapper.writeValueAsString(user);
    //解析Json为树形结构
    JsonNode node = mapper.readTree(json);
    String name = node.get("name").toText();
    //解析Json为指定类的对象
    User user1 = mapper.readValue(json,User.class);
}

更多使用情景可以参考Jackson-docs

Jackson的扩展注解

@JsonProperty

作用在类的属性上,为其添加一个别名,会影响序列化的属性名称

public class User implements Serializable{
    @JsonProperty("sName")
    private String name;
}
{"sName":"赵四"}

@JsonIgnore

作用在类的属性上,序列化时忽略某个属性

public class User implements Serializable{
    @JsonIgnore
    private String name; 
}

@JsonIgnoreProperties

作用在类上,批量忽略类属性

@JsonIgnoreProperties({"age","sex"})
public class User

注意,如果属性有别名,那么就写别名,比如要忽略之前的属性name,因为他有别名,所以应该在@JsonIgnoreProperteis中填写他的别名sName

@JsonFormat

作用在属性上,格式化属性
一般用来格式化日期

public class User implements Serializable{
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date date;
}

@JsonNaming

作用在类或者属性上,改变序列化属性的命名规则

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class User implements Serializable{
    private String userName;
}

上面的SnakeCaseStrategy规则,会将属性userName转为Json属性user-name

@JsonSerialize

作用在类上,代表指定自己的序列化规则类

@JsonSerialize(UserSerializer.class)
public class User implements Serializable{}

下面要实现自定义序列化规则,需要继承抽象类JsonSerializer

public class UserSerializer extends JsonSerializer<User>{
    @Override
    public void serialize(User user, JsonGenerator generator, SerializerProvider provider)
            throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeStringField("public-info", user.getPublicInfo());
        generator.writeEndObject();
    }
}

@JsonDeserialize

作用在类上,代表指定自己的反序列化规则类

@JsonDeserialize(UserDeserializer.class)
public class User implements Serializable{}

下面要实现自定义反序列化规则,需要继承抽象类JsonDeserializer

pbulic class UserDeserializer extends JsonDeserializer<User>{
    @Override
    public User deserialize(JsonParser parser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        JsonNode node = parser.getCodec().readTree(parser);
        String publicInfo = node.get("public-info").asText();
        User user = new User();
        user.setPublicInfo(publicInfo);
        return user;
    }
}

@JsonView

作用在类的属性上和返回User的方法上,比如返回该对象的controller方法,@JsonView可以让两个方法虽然返回的同一个实例,但是展示的字段却不相同

public class User implements Serializable{
    @JsonView(AllField.class)
    private String id;
    @JsonView(JustName.class)
    private String name;
    @JsonView(AllField.class)
    private String sex;
    
    public interface JustName{}
    public interface AllField extends JustName{}

}

用不同接口代表不同的view,使用继承的方式,可以避免一些重复的声明
下面是调用方法的申明:

@GetMapping("get/name")
@ResponseBody
@JsonView(User.JustName.class)
public User getUserName(){}

@GetMapping("get/all")
@ResponseBody
@JsonView(User.AllField.class)
public User getAllUserField(){}

假设上面两个方法的方法体完全相同,因为JsonView的原因,第一个方法只会返回{"name":"赵四"},而第二个方法则会返回四哥的所有信息

更多jackson注解参考jackson annotation

Jackson泛型

String json = "[{\"name\":\"赵四\"}]"
List<User> users = objectMapper.readValue(json,List.class);
User user = users.get(0);

上面的代码会在第三行的时候抛出异常:linkedHashMap cannot cast to User
因为objectMapper反序列化的时候并没有提供List的泛型,jackson内部并不知道list子元素是什么类型,所以默认转为了LinkedHashMap
jackson提供了泛型处理:

JavaType type = TypeFactory.defaultInstance.constructParametricType(List.class, User.class);
List<User> list = mapper.readValue(json, type);

除了上面的方式,还可以通过TypeReference类来处理,下面是一个将POJO转为Map的例子:

ObjectMapper mapper = new ObjectMapper();

// Convert POJO to Map
Map<String, Object> map = 
    mapper.convertValue(foo, new TypeReference<Map<String, Object>>() {});

// Convert Map to POJO
Foo anotherFoo = mapper.convertValue(map, Foo.class);