GVKun编程网logo

如何在SpringMVC中为特定类自定义JSON(springmvc自定义参数解析)

23

这篇文章主要围绕如何在SpringMVC中为特定类自定义JSON和springmvc自定义参数解析展开,旨在为您提供一份详细的参考资料。我们将全面介绍如何在SpringMVC中为特定类自定义JSON的

这篇文章主要围绕如何在SpringMVC中为特定类自定义JSONspringmvc自定义参数解析展开,旨在为您提供一份详细的参考资料。我们将全面介绍如何在SpringMVC中为特定类自定义JSON的优缺点,解答springmvc自定义参数解析的相关问题,同时也会为您带来19. SpringBoot 扩展 SpringMVC功能、 接管、自定义SpringMVC、blob类型数据如何在springmvc中获取显示、json – 在Spring 3 MVC下的Jackson自定义序列化、Spring MVC中使用FastJson自定义注解的实用方法。

本文目录一览:

如何在SpringMVC中为特定类自定义JSON(springmvc自定义参数解析)

如何在SpringMVC中为特定类自定义JSON(springmvc自定义参数解析)

我正在使用SpringMVC并具有以下方法。

@RequestMapping("/login")public @ResponseBody User login(User user) {    // some operation here ....    return user;}

在大多数情况下,SpringMVC以适当的方式将对象转换为JSON。但是有时您可能需要自定义JSON。在哪里可以为所有User对象自定义JSON。我希望将User对象转换为JSON的行为在所有方面都保持一致。我想一个监听器或接口可以实现。是否存在这种解决方案?

PS。如果我要转换的对象是第三方类的实例怎么办?我无法在类定义中自定义它,因为我没有源代码…

答案1

小编典典

Spring默认使用Jackson来序列化和反序列化JSON。您可以@JsonSerializeUser类型上使用Jackson的批注,并提供一个JsonSerializer实现所需序列化的实现。

19. SpringBoot 扩展 SpringMVC功能、 接管、自定义SpringMVC

19. SpringBoot 扩展 SpringMVC功能、 接管、自定义SpringMVC

编写一个配置类(@Configuration),是WebMvcConfigurerAdapter类型;不能标注@EnableWebMv:
既保留了所有的自动配置,也能用我们扩展的配置;

如下代码:【拓展 请求 映射 页面  功能】

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /bihu 请求来到 Thymeleaf 页面
        registry.addViewController("/bihu").setViewName("Thymeleaf");
    }
}

原理:

1)、WebMvcAutoConfiguration是SpringMVC的自动配置类

2)、在做其他自动配置时会导入;@Import(EnableWebMvcConfiguration.class)

@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//从容器中获取所有的WebMvcConfigurer
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
//一个参考实现;将所有的WebMvcConfigurer相关配置都来一起调用;
@Override
// public void addViewControllers(ViewControllerRegistry registry) {
// for (WebMvcConfigurer delegate : this.delegates) {
// delegate.addViewControllers(registry);
// }
}
}
}
EnableWebMvcConfiguration

3)、所以:容器中所有的WebMvcConfigurer都会一起起作用;

4)、我们的配置类也会被调用;

效果:SpringMVC的自动配置和我们的扩展配置都会起作用;

 

所以这就达成了 拓展SpringBoot 中 SpringMVC的功能

 



 

全面接管SpringBoot中的 SpringMVC:

SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己配置;所有的SpringMVC的自动配置都失效了

我们需要在配置类中添加@EnableWebMvc即可;

package com.bihu.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
//使用EnableWebMvc注解后 SpringBoot中SpringMVC的配置全部失效 都需要自己配,例如 下面的  请求bihu  跳到Thymeleaf页面 就是自己配的,,,你如果在static放(静态资源目录)html,也不可以直接访问
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /bihu 请求来到 Thymeleaf
        registry.addViewController("/bihu").setViewName("Thymeleaf");
    }
}

原理: 为什么@EnableWebMvc自动配置就失效了;

1)@EnableWebMvc的核心

@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {

2)、

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

3)、

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, dispatcherServlet.class,
WebMvcConfigurerAdapter.class })
//容器中没有这个组件的时候,这个自动配置类才生效  主要看这里!!!!这个条件注解这里
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ dispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

4)、@EnableWebMvc将WebMvcConfigurationSupport组件导入进来;

5)、导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

 

如何修改SpringBoot的默认配置

模式: 1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如 果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默 认的组合起来;

2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置

3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置

 

blob类型数据如何在springmvc中获取显示

blob类型数据如何在springmvc中获取显示

blob类型是数组类型,展示在前端需要转成字符串类型

使用方法可以在实体类中定义一个新的String类型的字段,然后通过get方法得到blob类型字段的值,然后后面的操作都使用这个新的字段即可

 private byte[] contentText;
    
    private String contentTextShow;
    
    public String getContentTextShow() {
        return new String(contentText,StandardCharsets.UTF_8);
    }

如果想要使用blob字段的部分数据,可以通过正则表达式去完成

json – 在Spring 3 MVC下的Jackson自定义序列化

json – 在Spring 3 MVC下的Jackson自定义序列化

我有几个POJO看起来像这样:

class Items {
    List

我希望能够使用自定义格式序列化Item中的Date字段(在日期中添加前缀,例如“Date:xxx”),但我不想总是这样做(因为它被其他消费者使用)它不需要此前缀),仅在特定情况下.

如果我使用@JsonSerialize(使用= CustomDateSerializer.class)注释Item的getInsertionDate()我可能会使这个工作,但是,我不想这样做,因为我不总是想要使用此方法序列化此字段,仅在特定情况下.

理想情况下,我会在我想要自定义序列化的控制器中执行此操作:

@JsonSerialize(using = CustomDateSerializer.class)
public List

其中CustomDateSerializer扩展了SerializerBase< Date>并且Jackson会发现它应该使用默认的序列化程序序列化List中的每个项目,当它遇到Date对象时它应该使用我的自定义序列化程序.当然这不起作用,因为不是如何使用@JsonSerialize,但有没有办法让这个工作不是用包装器包装Item并在我想要自定义序列化时使用该包装器?我是否以错误的方式思考这个问题,还有另一种方法可以做到这一点吗?

请注意,我正在使用Spring MVC,所以我不是直接调用序列化.

任何帮助将非常感激 :)

最佳答案
问题是如果它是服务端点方法,杰克逊没有在getItems()上看到注释;它通常只传递类型List< Item>春天决定.使用JAX-RS(如Jersey),可以传递与该方法相关的注释(也许Spring也有一些方法);虽然它需要来自集成代码(对于JAX-RS,Jackson JAX-RS JSON提供程序模块)的更多支持来传递它.

实际创建单独的POJO(而不是传递List类型)可能更容易,以便您可以添加必要的注释.

如果您直接使用Jackson,您还可以使用ObjectWriter并指定要使用的默认日期格式.但是我不知道Spring是否允许你这样做(大多数框架都没有,只暴露了ObjectMapper的可配置性).

还有一个注意事项 – 您可以使用Dates(和Jackson 2.x)上的简单注释代替自定义序列化程序(和/或反序列化程序):

public class DateStuff {
  @JsonFormat(shape=JsonFormat.Shape.STRING,pattern="'Date:'yyyy'-'MM'-'dd")
  public Date date;
}

指定每个属性格式覆盖.

Spring MVC中使用FastJson自定义注解

Spring MVC中使用FastJson自定义注解

最近在做.net转译成Java。其中遇到一个很蛋疼的问题。以前.net属性名都是首字母大写。造成返回给客户端的JSON字符串属性名称都是首字母大写。为了和前端对接我们以前都是如下图所示做法

public class User {

    @JSONField(name = "Name")
    private String name;
    @JSONField(name = "Age")
    private BigDecimal age;
    @JSONField(name = "Id")
    private String id;
    @JSONField(name = "isGirl")
    private boolean girl;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public BigDecimal getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public boolean isGirl() {
        return girl;
    }

    public void setGirl(boolean girl) {
        this.girl = girl;
    }
}

在每个属性上加上JSONField来定义属性名称,特别的繁琐而且还容易出错。下面我将使用FastJson的自定义注解,通过一个注解来实现。

首先用过继承 WebMvcConfigurationSupport 类来实现一个自定义配置类

package com.raiden;

import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.raiden.filter.DataToStringFilter;
import com.raiden.filter.FirstLetterCapitalizedFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;

@Configuration
public class ExtWebMvcConfigurerAdapter extends WebMvcConfigurationSupport {

    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //new一个自定义的转换器
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonMessageConverter();
        //过滤器链 其中2个是自定义的过滤器
        SerializeFilter[] filters = {new FirstLetterCapitalizedFilter(), new DataToStringFilter()};
        //将过滤器链放入自定义转换器中
        fastJsonHttpMessageConverter.getFastJsonConfig().setSerializeFilters(filters);
        //将转换器放入转换器链中
        converters.add(fastJsonHttpMessageConverter);
        //将转换器链放入配置管理器中
        super.configureMessageConverters(converters);
    }
}

下面是自定义转换器 其实很简单都不用做什么,只要简单的继承下就好了

package com.raiden;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

/**
 *自定义转换器
 */
public class FastJsonMessageConverter extends FastJsonHttpMessageConverter {

}

 

下面是2个自定义的过滤器

如果要处理属性名称则继承NameFilter

一下代码进行了第二次修订,主要是为了防止和JSONField注解冲突

 

package com.raiden.fastjson.filter;

import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.NameFilter;
import com.raiden.fastjson.util.FieldNameUtils;
import com.raiden.fastjson.annotation.FirstLetterCapitalized;
import com.raiden.fastjson.annotation.Ignore;
import com.raiden.fastjson.util.FieldUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;

/**
 * @创建人:Raiden
 * @Descriotion:该过滤器针对属性名,首字母大写过滤器
 * @Date:Created in 9:54 2019/6/22
 * @Modified By:
 */
public class FirstLetterCapitalizedFilter implements NameFilter {
    @Override
    public String process(Object instance, String name, Object value) {
        if (null == instance || StringUtils.isEmpty(name)){
            return name;
        }
        Class<?> clazz = instance.getClass();
        //判断类上是否有首字母大写的注解
        if (clazz.isAnnotationPresent(FirstLetterCapitalized.class)){
            //是否是boolean实例
            boolean isBooleanInstance = Boolean.class.isInstance(value);
            //通过名称获得改域 如果使用了JSONField自定义域名会出现找不到的情况
            Field field = FieldUtils.getField(clazz, name);
            if (null != field){
                //看看域上是否有忽略的注解和JSONField注解 或者有 忽略字段注解 如果有则不改变其属性名
                if (field.isAnnotationPresent(Ignore.class) || field.isAnnotationPresent(JSONField.class)){
                    return name;
                }else{
                    //判断下是不是布尔值 如果是切name不是以is开头的 首字母大写并在前面加上is
                    if (isBooleanInstance && !name.toLowerCase().startsWith("is")){
                        return "Is" + FieldNameUtils.firstLetterCapitalized(name);
                    }
                    //将属性名首字母大写返回
                    return FieldNameUtils.firstLetterCapitalized(name);
                }
            }
            //用JSONField自定义属性名称可能会找不到域 因此忽略此报错 返回自定义的名称就行
            return checkBoolean(clazz, name, isBooleanInstance);
        }
        return name;
    }

    private String checkBoolean(Class<?> clazz, String name,boolean isBooleanInstance){
        if (isBooleanInstance){
            //布尔值找不到域 存在2种可能1是用了JSONField注解 2 是使用了小写的is开头 如 isShow 这里的name会是show
            String fieldName = "is" + FieldNameUtils.firstLetterCapitalized(name);
            //所以拼装好名字之后 在尝试找一次域
            Field field = FieldUtils.getField(clazz, fieldName);
            //如果找到了返回 带is的
            if (null != field){
                return fieldName;
            }
        }
        //如果还是获取不到证明使用的是 JSONField注解
        return name;
    }
}

 

 

如果要处理属性内容 则继承 ValueFilter 有时候会遇到BigDecimal 中放入数字 导致序列化之后精度丢失 比如  new BigDecimal(113.880) 序列化之后成了 113.8799999999999954525264911353588104248046875

每个单独处理很麻烦。所以设计了该方式:

 

package com.raiden.fastjson.filter;

import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.raiden.fastjson.util.FieldNameUtils;
import com.raiden.fastjson.annotation.DataToString;
import com.raiden.fastjson.annotation.FirstLetterCapitalized;
import com.raiden.fastjson.util.FieldUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
/**
 * @创建人:Raiden
 * @Descriotion:自定义BigDecimal序列化,精度值处理过滤器
 * @Date:Created in 9:54 2019/6/22
 * @Modified By:
 */
public class DataToStringFilter implements ValueFilter {
    @Override
    public Object process(Object instance, String name, Object value) {
        if (null == instance || StringUtils.isEmpty(name) || null == value){
            return value;
        }
        //判断下实例是不是BigDecimal 或者是 Double
        if (value instanceof Double || value instanceof BigDecimal){
            Class<?> instanceClazz = instance.getClass();
            //如果存在这个注解说明类名可能被更改
            if (instanceClazz.isAnnotationPresent(FirstLetterCapitalized.class)){
                name = FieldNameUtils.firstLetterLowercase(name);
            }
            //如果是则获取该域 如果使用了JSONField自定义域名会出现找不到报错的情况
            Field field = FieldUtils.getField(instanceClazz, name);
            if (null == field){
                field = getField(instanceClazz, name);
            }
            //检查该域是否有 DataToString注解
            if (null != field && field.isAnnotationPresent(DataToString.class)){
                return valueFormat(value, field);
            }
        }
        return value;
    }

    /**
     * 属性格式化
     * @param value
     * @param field
     * @return
     */
    private Object valueFormat(Object value,Field field){
        //获取DataToString注解
        DataToString dataToString = field.getAnnotation(DataToString.class);
        //获取保留小数位
        int newScale = dataToString.newScale();
        //获取舍入策略
        int roundingMode = dataToString.roundingMode();
        if (value instanceof Double){
            return new BigDecimal((Double) value).setScale(newScale, roundingMode).toString();
        }
        //返回保留值
        return ((BigDecimal) value).setScale(newScale, roundingMode).toString();
    }

    /**
     * 获取真正的属性
     * @param instanceClazz
     * @param name
     * @return
     */
    private Field getField(Class<?> instanceClazz,String name){
        Class<?> superclass = instanceClazz.getSuperclass();
        if (null == superclass){
            //父类为空证明该类为Object 不递归了返回吧
            return null;
        }
        //遍历全部的域
        Field[] fields = instanceClazz.getDeclaredFields();
        for (Field field : fields){
            if (!field.isAnnotationPresent(JSONField.class)){
                continue;
            }
            JSONField jsonField = field.getAnnotation(JSONField.class);
            if (name.equals(jsonField.name())){return field;
            }
        }
        return getField(superclass, name);
    }
}

 

属性名称工具类:
package com.raiden.fastjson;

/**
 * @创建人:Raiden
 * @Descriotion: 属性名称工具类
 * @Date:Created in 21:26 2019/6/23
 * @Modified By:
 */
public class FieldNameUtils {

    /**
     * 首字母大写的方法
     * @param name
     * @return
     */
    public static String firstLetterCapitalized(String name){
        char[] chars = name.toCharArray();
        StringBuilder builder = new StringBuilder();
        char c = chars[0];
        //如果是小写才替换
        if (c > 96 && c < 123){
            c -= 32;
            chars[0] = c;

        }
        builder.append(chars);
        return builder.toString();
    }

    /**
     * 首字母小写
     * @param name
     * @return
     */
    public static String firstLetterLowercase(String name){
        char[] chars = name.toCharArray();
        StringBuilder builder = new StringBuilder();
        char c = chars[0];
        //如果是小写才替换
        if (c > 64 && c < 91){
            c += 32;
            chars[0] = c;

        }
        builder.append(chars);
        return builder.toString();
    }
}

 

package com.raiden.fastjson.util;

import java.lang.reflect.Field;

/**
 * @创建人:Raiden
 * @Descriotion:
 * @Date:Created in 23:11 2019/7/4
 * @Modified By:
 */
public class FieldUtils {

    /**
     * 递归获取域 子类找不到找父类 直到直到或者 递归到Object为止
     * @param clazz
     * @param fieldName
     * @return
     */
    public static Field getField(Class<?> clazz, String fieldName){
        //获取父类class
        Class<?> superclass = clazz.getSuperclass();
        if (null == superclass){
            //父类为空证明该类为Object 不递归了返回吧
            return null;
        }
        Field declaredField = null;
        try {
            //忽略报错
            declaredField = clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            //此处忽略报错 递归查找
            return getField(superclass, fieldName);
        }
        //找到了返回
        return declaredField;
    }
}

 

下面是注解部分

package com.raiden.annotation;

import java.lang.annotation.*;

/**
 * 该注解的作用是让FastJson序列化的时候 将所有熟悉的首字母大写
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstLetterCapitalized {
}
package com.raiden.annotation;

import java.lang.annotation.*;
import java.math.BigDecimal;

/**
 * 用于解决BigDecimal序列化精度问题
 * 将BigDecimal转成String
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DataToString {
    //默认保留3位小数
    int newScale() default 3;
    //默认使用四舍五入
    int roundingMode() default BigDecimal.ROUND_HALF_UP;
}
package com.raiden.annotation;

import java.lang.annotation.*;

/**
 * 忽略该属性注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Ignore {
}

测试代码:

package com.raiden.controller;

import com.raiden.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@RestController
public class UserController {

    @GetMapping("getUser")
    public User getUser(){
        User user = new User();
        user.setId("1");
        user.setName("zhangsan");
        user.setAge(new BigDecimal(113.880));
        return user;
    }
}
package com.raiden.model;

import com.alibaba.fastjson.annotation.JSONField;
import com.raiden.annotation.DataToString;
import com.raiden.annotation.FirstLetterCapitalized;
import com.raiden.annotation.Ignore;
import com.raiden.annotation.Range;

import java.math.BigDecimal;

@FirstLetterCapitalized
public class User {

    @Ignore
    private String name;
    @DataToString(newScale = 3,roundingMode = BigDecimal.ROUND_HALF_UP)
    private BigDecimal age;
    @JSONField(name = "userId")
    private String id;
    private boolean girl;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public BigDecimal getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public boolean isGirl() {
        return girl;
    }

    public void setGirl(boolean girl) {
        this.girl = girl;
    }
}
package com.raiden;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

    public static void main(String[] arg){
        SpringApplication.run(App.class, arg);
    }
}

第一次写博客,有什么问题还望大佬们指正。代码多次修改如果跑不起来 可以去GitHub下载代码。谢谢

附上github连接:https://github.com/RaidenXin/FastJsonDemo/tree/master

关于如何在SpringMVC中为特定类自定义JSONspringmvc自定义参数解析的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于19. SpringBoot 扩展 SpringMVC功能、 接管、自定义SpringMVC、blob类型数据如何在springmvc中获取显示、json – 在Spring 3 MVC下的Jackson自定义序列化、Spring MVC中使用FastJson自定义注解的相关信息,请在本站寻找。

本文标签: