得到和获取都是有代价的,最孤独最痛苦的那段时光其实是你成长速度最快的时候,不要在奋斗的年纪选择享受,我们要知道生命的天平总在合理的范围内摆动,付出了才有得到的机会。
Spring Boot内置了jackson来完成JSON的序列化和反序列化操作,而且,在与其他技术集成的时候,比如Redis,MongoDB,Elasticsearch等对象序列化,默认都是使用jackson来完成的。
在Controller中,方法注解为@ResponseBody,则自动将方法返回的对象序列化成JSON。如果想自定义一个ObjectMapper来代替默认的,可以使用Java Config,使用@Bean来配置一个。
杰克逊使用ObjectMapper类将POJO对象序列化成JSON字符串,也能将JSON字符串反序列化成POJO对象.JackSon支持三种层次的序列化和反序列化方式。
Jackson遍历
树遍历方式通常适合没有POJO对象的JSON,代码如下:
@Autowired
private ObjectMapper mapper;
@GetMapping("/readtree.json")
public @ResponseBody String readTree() throws IOException{
String json = "{\"name\":\"lijz\",\"id\":10}";
JsonNode node= mapper.readTree(json);
String name = node.get("name").asText();
int id = node.get("id").asInt();
return "name:"+name+",id:"+id;
}
readTree方法可以接受一个字符串或者字节数组,文件,InputStream等,返回JsonNode作为根节点,可以像操作XML DOM那样操作遍历JsonNode以获取数据。
JsonNode支持以下方法来读取JSON数据:
对象绑定
应用程序更常见的是使用的的Java对象来与JSON数据互相绑定,仅仅调用ObjectMapper的readValue来实现,比如上一个例子中,可以创建一个POJO对象来与JSON相对应,POJO如下:
public class User {
Long id;
String name;
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;}
}
然后使用readValue来反序列化上面的JSON字符串:
@Autowired
private ObjectMapper mapper;
@GetMapping("/dataBind.json")
public @ResponseBody String dataBind() throws IOException{
String json = "{\"name\":\"lijz\",\"id\":10}";
User user = mapper.readValue(json, User.class);
return "name:"+user.getName()+",id:"+user.getId();
}
将POJO序列化成JSON,使用映射的writeValueAsString方法:
@Autowired
private ObjectMapper mapper;
@GetMapping("/serialization.json")
public @ResponseBody String dataBind() throws IOException{
User user = new User();
user.setName("scg");
user.setId((long) 18);
String jsonStr = mapper.writeValueAsString(user);
return jsonStr;
}
流式操作
树模型和数据绑定都是基于流式操作完成的,即通过JsonParser类解析JSON,形成JsonToken流:
@Autowired
private ObjectMapper mapper;
@GetMapping("/parser.html")
public @ResponseBody String parser() throws IOException{
String json = "{\"name\":\"lijz\",\"id\":10}";
JsonFactory fc = mapper.getFactory();
String key = null,value = null;
JsonParser parser = fc.createParser(json);
//{,START_OBJECT,忽略第一个Token
JsonToken token = parser.nextToken();
int i =0;
String ret = "";
while(i
JsonParser的解析结果包含了一系列JsonToken,JsonToken是一个枚举类型,常用的START_BOJECT代表符号“{”; START_ARRAY和END_ARRAY代表“[”和“]”,FIELD_NAME表示一个JSON Key; VALUE_STRING代表一个JSON Value,字符串类型; VALUE_NUMBER_INT则表示一个整数类型。
判断令牌的类型后,通过调用的的getValueAsXxx来获取其值,XXX是其值的类型。
@Autowired
private ObjectMapper mapper;
/**
* 序列化
*/
@RequestMapping("/generator.html")
public @ResponseBody String generator() throws Exception{
JsonFactory fc = mapper.getFactory();
//输出到StringWriter
StringWriter sw = new StringWriter();
JsonGenerator g = fc.createGenerator(sw);
g.writeStartObject();
//"message","Hello world!"
g.writeStringField("name", "scg");
g.writeEndObject();
g.close();
return sw.toString();
}
杰克逊注解
杰克逊包含了很多注解,用来个性化序列化和反序列化操作,主要有如下注解。
@JsonProperty,作用在属性上,用来为JSON Key指定一个别名。
@JsonProperty("userName")
private String name;
@JsonIgnore,作用在属性上,用来忽略此属性。
@JsonIgnore
private String age;
@JsonIgnoreProperties,忽略一组属性,作用于类上,比如@JsonIgnoreProperties({“ID”,”照片”})。
@JsonIgnoreProperties({"id","photo"})
public class User {}
@JsonAnySetter,标记在某个方法上,此方法接受键,值两个参数,用于杰克逊在反序列化过程中,未找到的对应属性都调用次方法。通常这个方法用一个图来实现。
private Map mp = new HashMap();
@JsonAnySetter
public void other(String property,String value){
mp.put(property, value);
}
@JsonAnyGetter,此注解标注在一个返回地图的方法上,杰克逊会取出地图中的每一个值进行序列化。
@JsonFormat,用于日期格式化。
@JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
private Date d;
@JsonNaming,用于指定一个命名策略,作用于类或者属性上,类似@JsonProperty,但是自动命名.Jackson自带了多种命名策略,你可以实现自己的命名策略,比如输出的键由Java的的命名方式转为下划线命名方法--username转化为USER_NAME。
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class User {
……
}
@JsonSerialize,指定一个实现类来自定义序列化。类必须实现JsonSerializer接口
public class UserSerializer extends JsonSerializer {
@Override
public void serialize(User user, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeStringField("user-name", user.getName());
jgen.writeEndObject();
}
}
JsonGenerator对象是杰克逊底层的序列化实现,上面的代码中我们仅仅序列化的名称属性,且输出的关键是用户名使用注解@JsonSerializer来指定用户对象的序列化方式:
@JsonSerialize(using = UserSerializer.class)
public class User {
……
}
@JsonDeserialize,用户自定义反序列化,同JsonSerialize,需要实现JsonDeserializer接口。
public class UserDeserializer extends JsonDeserializer {
@Override
public User deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String name = node.get("user-name").asText();
User user = new User();
user.setName(name);
return user;
}
}
@JsonDeserialize(using = UserDeserializer.class)
public class User {
……
}
@JsonView,作用在类或者属性上,用来定义一个序列化组.Spring MVC的控制器方法可以使用同样的@JsonView来序列化属于这一组的配置。比如对于用户对象,某些情况下只返回ID属性就行,而某些情况下需要返回编号和名称代码如下:
public class User {
public interface IdView{};
public interface IdNameView extends IdView{};
@JsonView(IdView.class)
private Integer id;
@JsonView(IdNameView.class)
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* controller
*/
@JsonView(User.IdView.class)
@RequestMapping("/id.json")
public @ResponseBody User queryIds(){
User user = new User();
user.setId(1);
user.setName("scg");
return user;
}
用户定义了两个接口类,一个为IdView,另外一个为IdNameView,继承了IdView接口。这两个接口代表了两个序列化组的名次。属性ID使用了@JsonView(IdView.class),而属性name使用了@JsonView(IdNameView.class).Spring MVC的Controller方法允许使用JsonView()指定一个组名,被序列化的对象只有在这个组的属性才会被序列化。以上代码只输出id属性,如果换成@JsonView(User.IdNameView.class),则输出名称属性。同时,ID属性的组名IdView.class因为是IdNameView.class的父类,同样也会输出。