springboot+jackson+hibernatevalidator实现json转嵌套bean(赋值与校验)

最近小明遇到一个需求,写一个restful的接口,将一大坨json数据解析,并按指定逻辑入库,然后小明花了大量时间写json解析和校验,为了可以早点下班,这里提供springboot+jackson+hibernatevalidator的简单实现。@小明。

废话不多说,直接上案例,解决:1)json自动解析并匹配java bean;2)基于hibernatevalidator注解的校验;3)嵌套json自动解析到嵌套的java bean(含list);4)嵌套的java bean的hibernatevalidator注解的校验;5)大写风格的json到驼峰风格的java属性自动转换。

1、新建一个简单的springboot项目

添加如下依赖即可,会自动将jackson和hibernatevalidator依赖引入。

<dependency>
	 <groupId>org.springframework.boot</groupId>
	 <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2、创建三个实体bean,GrandFather、Father、Son,依次嵌套。

Son

@JsonNaming(UpperSnakeCaseStrategy.class)
public class Son implements Serializable{

	private static final long serialVersionUID = 1L;
	
	@Length(min = 4, max = 16, message = "用户名长度要求在{min}-{max}之间")
    @NotNull(message = "名不可为空")
	private String name;
	
	@Min(value = 18, message = "未成年不满足注册要求")
    @Max(value = 80, message = "年龄错误")
	private Integer age;
	
	@DecimalMin(value = "0.5", message = "钱必须大于0.5")
    @DecimalMax(value = "1000.2", message = "钱必须小于1000.2")
	private Double money;
	
	@DateTimeFormat(pattern = "yyyy-MM-dd") //将前端传的string转date,指定yyyy-MM-dd格式
	@JsonFormat(pattern = "yyyy-MM-dd") //@responseBody标注后,后端往前端返时,json转化指定格式
	@Past(message = "出生日期错误")
	//@JsonSetter("BIRTH_DAY")
	private Date birthDay;
    public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Double getMoney() {
		return money;
	}
	public void setMoney(Double money) {
		this.money = money;
	}
	public Date getBirthDay() {
		return birthDay;
	}
	public void setBirthDay(Date birthDay) {
		this.birthDay = birthDay;
	}
	
	@Override
	public String toString() {
		return "Son [name=" + name + ", age=" + age + ", money=" + money + ", birthDay=" + birthDay + "]";
	}
}

Father,注意类的属性必须加上@Valid,这样才可以继续递归校验。

@JsonNaming(UpperSnakeCaseStrategy.class)
public class Father implements Serializable{

	private static final long serialVersionUID = 1L;
	
	@Length(min = 4, max = 16, message = "用户名长度要求在{min}-{max}之间")
    @NotNull(message = "名不可为空")
	private String name;
	
	@Min(value = 18, message = "未成年不满足注册要求")
    @Max(value = 80, message = "年龄错误")
	private Integer age;
	
	@DecimalMin(value = "0.5", message = "钱必须大于0.5")
    @DecimalMax(value = "1000.2", message = "钱必须小于1000.2")
	private Double money;
	
	@DateTimeFormat(pattern = "yyyy-MM-dd") //将前端传的string转date,指定yyyy-MM-dd格式
	@JsonFormat(pattern = "yyyy-MM-dd") //@responseBody标注后,后端往前端返时,json转化指定格式
	@Past(message = "出生日期错误")
	//@JsonSetter("BIRTH_DAY")
	private Date birthDay;
	
	@Valid
	//@JsonSetter("SON_LIST")
	private List<Son> sonList;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Double getMoney() {
		return money;
	}

	public void setMoney(Double money) {
		this.money = money;
	}

	public Date getBirthDay() {
		return birthDay;
	}

	public void setBirthDay(Date birthDay) {
		this.birthDay = birthDay;
	}

	public List<Son> getSonList() {
		return sonList;
	}

	public void setSonList(List<Son> sonList) {
		this.sonList = sonList;
	}

	@Override
	public String toString() {
		return "Father [name=" + name + ", age=" + age + ", money=" + money + ", birthDay=" + birthDay + ", sonList="
				+ sonList + "]";
	}
}

GrandFather

@JsonNaming(UpperSnakeCaseStrategy.class)
public class GrandFather implements Serializable{

	private static final long serialVersionUID = 1L;
	
	@Length(min = 4, max = 16, message = "用户名长度要求在{min}-{max}之间")
    @NotNull(message = "名不可为空")
	private String name;
	
	@Min(value = 18, message = "未成年不满足注册要求")
    @Max(value = 80, message = "年龄错误")
	private Integer age;
	
	@DecimalMin(value = "0.5", message = "钱必须大于0.5")
    @DecimalMax(value = "1000.2", message = "钱必须小于1000.2")
	private Double money;
	
	@DateTimeFormat(pattern = "yyyy-MM-dd") //将前端传的string转date,指定yyyy-MM-dd格式
	@JsonFormat(pattern = "yyyy-MM-dd") //@responseBody标注后,后端往前端返时,json转化指定格式
	@Past(message = "出生日期错误")
	//@JsonSetter("BIRTH_DAY")
	private Date birthDay;
	
	@Valid
	@NotNull(message = "名不可为空")
	//@JsonSetter("FATHER")
	private Father father;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Double getMoney() {
		return money;
	}

	public void setMoney(Double money) {
		this.money = money;
	}

	public Date getBirthDay() {
		return birthDay;
	}

	public void setBirthDay(Date birthDay) {
		this.birthDay = birthDay;
	}

	public Father getFather() {
		return father;
	}

	public void setFather(Father father) {
		this.father = father;
	}

	@Override
	public String toString() {
		return "GrandFather [name=" + name + ", age=" + age + ", money=" + money + ", birthDay=" + birthDay
				+ ", father=" + father + "]";
	}
}

3、创建controller,这里使用springMVC的自动参数绑定,BindingResult会收集校验失败的信息。

@RestController
@RequestMapping("json")
public class JsonValidController {

	@PostMapping("insert-order")
	public ResultData insertOrder(@Valid @RequestBody GrandFather grandFather, BindingResult bindingResult) {
		ResultData rd = new ResultData();
        if (bindingResult.hasErrors()) {
        	rd.setCode("201");
        	rd.setMessage("校验失败");
            List<ObjectError> list = bindingResult.getAllErrors();
            rd.setData(list);
            return rd;
        }else {
        	System.out.println(grandFather);
            return ResultData.ok("ok");
        }
	}
}

4、重写方法,名称转换策略,这里提供UpperSnakeCaseStrategy,将类似ABC_DEF自动赋值到abcDef的属性上。当然也可以通过类似@JsonSetter("BIRTH_DAY")和@JsonGetter("BIRTH_DAY") 实现具体的每个属性的转化(JsonSetter用于json反序列化,JsonGetter用于json序列化)。

import com.fasterxml.jackson.databind.PropertyNamingStrategy.PropertyNamingStrategyBase;
public class UpperSnakeCaseStrategy extends PropertyNamingStrategyBase
{
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
    public String translate(String input)
    {
        if (input == null) return input; // garbage in, garbage out
        int length = input.length();
        StringBuilder result = new StringBuilder(length * 2);
        int resultLength = 0;
        boolean wasPrevTranslated = false;
        for (int i = 0; i < length; i++)
        {
            char c = input.charAt(i);
            if (i > 0 || c != '_') // skip first starting underscore
            {
                if (Character.isUpperCase(c))
                {
                    if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
                    {
                        result.append('_');
                        resultLength++;
                    }
                    wasPrevTranslated = true;
                }
                else
                {
                	c = Character.toUpperCase(c);
                    wasPrevTranslated = false;
                }
                result.append(c);
                resultLength++;
            }
        }
        
        return resultLength > 0 ? result.toString() : input;
    }
}

5、测试,可以实现相关功能。

{
	"NAME":"GRANDFATHER",
	"AGE":71,
	"MONEY":100.09,
	"BIRTH_DAY":"1937-08-01",
	"FATHER":{
		"NAME":"FATHER",
		"AGE":41,
		"MONEY":200.09,
		"BIRTH_DAY":"1963-07-15",
		"SON_LIST":[
				{
					"NAME":"SON",
					"AGE":21,
					"MONEY":1.09,
					"BIRTH_DAY":"1991-01-15"
				},
				{
					"NAME":"SON2",
					"AGE":22,
					"MONEY":2.09,
					"BIRTH_DAY":"1992"
				},
				{
					"NAME":"SON3",
					"AGE":23,
					"MONEY":3.09,
					"BIRTH_DAY":"1993-03-15"
				}
			]
	}
}

最后,使用@Valid实现嵌套校验,就无法使用spring封装的分组校验了。估计这种需要两用的奇葩问题,以后升级了可以实现吧。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Spring Boot和Netty中实现发送Json消息,你可以使用Net的`ChannelHandlerContext`对象来发送消息。下面是一个简单的示例: 首先,创建一个`JsonMessage`类表示要发送的Json消息: ```java public class JsonMessage { private String message // 省略构造方法和getter/set @Override public String toString() { return "JsonMessage{" + "message='" + message + '\'' + '}'; } } ``` 然后,在`JsonHandler`中添加逻辑以接收和发送Json消息。下面是一个示例: ```java import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class JsonHandler extends SimpleChannelInboundHandler<String> { private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { // 接收到客户端发送的Json字符串 System.out.println("Received message: " + msg); // 解析Json字符串为对象 JsonMessage jsonMessage = objectMapper.readValue(msg, JsonMessage.class); // 处理消息逻辑 // ... // 构造要发送的Json消息对象 JsonMessage response = new JsonMessage("Hello from server!"); // 将Json消息对象换为字符串 String responseJson = objectMapper.writeValueAsString(response); // 发送Json消息给客户端 ByteBuf buf = ctx.alloc().buffer(); buf.writeBytes(responseJson.getBytes()); ctx.writeAndFlush(buf); } } ``` 在上面的示例中,我们将接收到的Json字符串解析为`JsonMessage`对象,并构造要发送的响应消息。然后,我们使用`ObjectMapper`将响应消息对象换为Json字符串,并通过`ChannelHandlerContext`发送给客户端。 需要注意的是,你可以根据具体的业务需求在`channelRead0`方法中添加适当的处理逻辑。并且在实际应用中,你可能还需要处理异常、断开连接等其他情况。 最后,你可以通过编写Spring Boot的启动类来启动Netty服务器,并监听指定的端口: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); int port = 8080; try { new JsonServer(port).run(); } catch (Exception e) { e.printStackTrace(); } } } ``` 在上面的示例中,我们在Spring Boot应用的启动类中创建了一个新的`JsonServer`实例,并通过调用`run`方法来启动Netty服务器。 现在,你可以运行Spring Boot应用,并发送Json消息到服务器,然后服务器会将响应消息发送回客户端。记得根据自己的实际需求进行适当的修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值