Spring Boot2 JSON技术

得到和获取都是有代价的,最孤独最痛苦的那段时光其实是你成长速度最快的时候,不要在奋斗的年纪选择享受,我们要知道生命的天平总在合理的范围内摆动,付出了才有得到的机会。


Spring Boot内置了jackson来完成JSON的序列化和反序列化操作,而且,在与其他技术集成的时候,比如Redis,MongoDB,Elasticsearch等对象序列化,默认都是使用jackson来完成的。

在Controller中,方法注解为@ResponseBody,则自动将方法返回的对象序列化成JSON。如果想自定义一个ObjectMapper来代替默认的,可以使用Java Config,使用@Bean来配置一个。

杰克逊使用ObjectMapper类将POJO对象序列化成JSON字符串,也能将JSON字符串反序列化成POJO对象.JackSon支持三种层次的序列化和反序列化方式。

  • 采用JsonParser来解析JSON,解析结果是一串令牌,采用JsonGenerator来生成JSON,这是最底层的方式。
  • 采用树遍历方式,JSON被读入到JsonNode对象中,可以像操作XML DOM那样读取JSON。
  • 采用的的DataBind方式,将POJO序列化成JSON,或者反序列化到POJO,这是最直接和最简单的一种方式,不过有时候需要辅助杰克逊的注解或者上述序列化实现类来个性化序列化和反序列化操作。

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数据:

  • asXXX,比如asText,asBoolean,asInt中中等,读取JsonNode对应的值。
  • IsArray的的,用于判断JsonNode是否是数组,如果是数组,则可以调用GET(我)来进行遍历,通过大小()来获取长度。
  • 得到(字符串),获取当前节点的字节点,返回JsonNode,如以上代码所示。

对象绑定

应用程序更常见的是使用的的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的父类,同样也会输出。

你可能感兴趣的:(微服务)