• Spring Boot 之遇见JSON


    MVC框架中,Spring Boot内置了jackson来完成JSON的序列化和反序列化操作,并且,在与其他技术集成的时候,如Redis、MongoDB、Elasticsearch等对象序列化,都可使用jackson来完成。

    Spring Boot中使用Jackson

    在Controller中,方法注解为@ResponseBody,则自动将方法返回的对象序列化成JSON。

    @Controller
    @RequestMapping("/json")
    public class JsonController(){
    	@Autowired UserService userService;
    	@GetMapping("/user/{id}.json")
    	public @ResponseBody User showUserInfo(@PathVariable Long id){
    		User user = userService.getUserById(id);
    		return user;
    	}
    }
    

    自定义ObjectMapper

    使用Java Config来自定义一个ObjectMapper,使用@Bean来配置,如:

    @Configuration
    public class JacksonConf{
    	@Bean
    	public ObjectMapper getObjectMapper() {
    		ObjectMapper objectMapper = new ObjectMapper();
    		objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    		return objectMapper;
    	}
    }
    

    获取当前时间:

    @GetMapping("/now.json")
    public @ResopnseBody Map datetime() {
    	Map map = new HashMap ();
    	map.put("time", new Date());
    	return map;
    }
    

    当访问now.json时,输出:

    {"time": "2019-03-17 11:11:11"}
    

    Jackson的三种使用方式

    • 采用JsonParser来解析JSON,解析结果是一串Tokens,采用JsonGenerator来生成JSON,这是最底层的方式。
    • 采用树遍历(Tree Traversing)方式,JSON被读入到JsonNode对象中,可以像操作XML DOM那样读取JSON。
    • 采用DataBind方式,将POJO序列化成JSON,或者反序列化到POJO。

    Jackson树遍历

    树遍历通常适合没有POJO对应的JSON,如:

    @Autowired ObjectMapper mapper;
    
    @GetMapping("/readtree.json")
    public @ResponseBody String readtree() throws JsonProcessingException,IOException{
    	String json = "{"name":"aaa","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作为根节点。

    JsonNode支持以下方法来读取JSON数据:

    • asXXX,如asText、asBoolean、asInt等,读取JsonNode对应的值。
    • isArray,用于判断JsonNode是否是数组,如果是数组,可以用get(i)来进行遍历,通过size()来获取长度。
    • get(String),获取当前节点的子节点,返回JsonNode。

    JSON规范要求Key是字符串,且用双引号

    对象绑定

    应用程序更多的是使用Java对象与JSON数据互相绑定,仅仅调用ObjectMapper的readValue来实现,如:

    {"name":"aaa",age:10}
    

    可以创建一个POJO对象来与JSON相对应:

    public class User {
    	Long id;
    	String name;
    	// setter getter
    }
    

    然后使用readValue来反序列化上面的JSON字符串:

    @GetMapping("/databind.json")
    public @ResponseBody String databind() throws JsonProcessingException,IOException{
    	String json = "{"name":"aaa","id":10}";
    	User user = mapper.readValue(json,User.class);
    	return "name:"+user.getName()+",id:"+user.getId();
    }
    

    将POJO序列化成JSON,使用mapper的writeValueAsString方法:

    @GetMapping("/serialization.json")
    public @ResponseBody String custom() throws JsonProcessingException {
    	User user = new User();
    	user.setId(11);
    	user.setName("hello");
    	String str = mapper.writeValueAsString(user);
    	return str;
    }
    

    mapper.writeValueAsString将对象序列化成JSON字符串,可以使用Jackson注解来对序列化的字段进行定制。

    流式操作

    树模型和数据绑定都是基于流式操作完成的,即通过JsonParser来解析JSON,形成JsonToken流,下面的代码是解析JSON:

    @Autowired
    ObjectMapper mapper;
    
    @RequestMapping("/parser.html")
    public @ResponseBody String parser() throws JsonParseException,IOException{
    	String json = "{"name":"aaa","id":10}";
    	JsonFactory f = mapper.getFactory();
    	String key = null,value=null;
    	JsonParser parser = f.createParser(json);
    	JsonToken token = parser.nextToken();
    	if(token == JsonToken.FIELD_NAME){
    		key = parser.getCurrentName();
    	}
    	token = parser.nextToken();
    	value = parser.getValueAsString();
    	parser.close();
    	return key+","+value;
    }
    

    JsonParser的解析结果包含了一系列JsonToken,JsonToken是一个枚举类型,常用的START_OBJECT代表符号“{”;START_ARRAY和END_ARRAY代表"[“和”]",FIELD_NAME表示一个JSON Key;VALUE_STRING代表一个JSON Value,字符串类型;VALUE_NUMBER则表示一个整数类型。

    判断Token的类型后,通过调用getValueAsXXX来获取其值,XXX是其值的类型。

    @RequestMapping("/generator.html")
    public @ResponseBody String generator() throws JsonParseException,IOException{
    	JsonFactory f = mapper.getFactory();
    	StringWriter sw = new StringWriter();
    	JsonGenerator g = f.createGenerator(sw);
    	g.writeStartObject();
    	g.writeStringField("name","aaa");
    	g.writeEndObject();
    	g.close();
    	return sw.toString();
    }
    

    Jackson注解

    Jackson包含了很多注解,用来个性化序列化和反序列化,如:

    @JsonProperty,作用在属性上,用来为JSON Key指定一个别名。

    @JsonProperty("userName")
    private String name
    

    @JsonIgnore,作用在属性上,用来忽略此属性。

    @JsonIgnore
    private String pwd
    

    @JsonIgnoreProperties,忽略一组属性,作用在类上;

    @JsonIgnoreProperties
    public static class SamplePojo{
    }
    

    @JsonAnySetter,标记在某个方法上,此方法接受Key、Value两个参数,用于JackSon在反序列化过程中,未找到的对应属性都调用此方法,通常用一个map来实现:

    @JsonAnySetter
    private void other( String property, Object value ) {
    	map.put(property,value);
    }
    

    @JsonAnyGetter,此注解标注在一个返回Map的方法上,JackSon会取出Map中的每一个值进行序列化。

    Class Department {
    	Map map = new HashMap();
    	int id;
    	public Department(int id){
    		this.id = id;
    		map.put("newAttr",1);
    	}
    	@JsonAnyGetter
    	public Map<String,Object> getOtherProperties() {
    		return map;
    	}
    }
    

    Department,对象序列化的时候,其JSON类似:

    {“id”:1,“newAttr”:1}
    

    @JsonFormat,用于日期格式,

    @JsonFormat(pattern = "yyyy-MM-dd HH-mm-ss")
    private Date createDate;
    

    @JsonNaming,用于指定一个命名策略,作用于类或者属性上。
    @JsonSerialize,指定一个实现类来自定义序列化。类必须实现JsonSerializer接口。
    @JsonDeserialize,用户自定义反序列化。

    @JsonView,作用在类或者属性上,用来自定义一个序列化组。比如对于User对象,某些情况下只返回id,而某些情况下需要返回id和name;

    public class User {
    	public interface IdView {};
    	public interface IdNameView extends IdView {};
    
    	@JsonView(IdView.class)
    	private Interger id;
    	@JsonView(IdNameView.class)
    	private String name;
    }
    

    集合的反序列化

    在Spring MVC的Controller方法中,可以使用@RequestBody将提交的JSON自动映射到方法参数上,如:

    @RequestMapping("/updateUsers.json")
    public @ResponseBody String say(@RequestBody List<User> list){
    	StringBuilder sb = new StringBuilder();
    	for(User user:list){
    		sb.append(user.getName()).append(" ");
    	}
    	return sb.toString();
    }
    

    接受如下一个JSON请求,并自动映射到User对象上。

    [
    	{
    		"name":"aaa",
    		"id":1
    	}
    	{
    		"name":"bbb",
    		"id":2
    	}
    ]
    

    Spring Boot能自动识别出List对象包含的是User类,因为在方法中定义的泛型的类型会被保留在字节码中,所以Spring Boot能识别List包含的泛型类型从而能正确反序列化。

  • 相关阅读:
    汇编随笔(第一章 基础知识)
    排序集锦(rough)
    如何让编写的Java代码规范整洁
    二分法查询数据java实现
    归并排序的学习与java实现
    小白的软件测试之路
    PyInstaller打包python脚本
    数据库SQL优化总结
    JQuery选择器
    软件测试面试题
  • 原文地址:https://www.cnblogs.com/aixing/p/13327476.html
Copyright © 2020-2023  润新知