如何使用 Jackson 全局定义命名约定

Posted

技术标签:

【中文标题】如何使用 Jackson 全局定义命名约定【英文标题】:how to globally define the naming convention with Jackson 【发布时间】:2016-12-23 16:45:41 【问题描述】:

我在 Spring 中使用 Jackson。我有一些这样的方法:

@RequestMapping(value = "/myURL", method = RequestMethod.GET)
public @ResponseBody Foo getFoo()  
    // get foo
    return foo;

被序列化的类 Foo 很大并且有很多成员。序列化没问题,使用注解或者自定义序列化器。

我唯一想不通的是如何定义命名约定。我想对所有序列化使用snake_case。

那么如何全局定义序列化的命名约定?

如果不可能,则必须使用本地解决方案。

【问题讨论】:

【参考方案1】:

不确定如何在全局范围内执行此操作,但这是一种在 JSON 对象级别而不是针对每个单独属性的方法:

@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Foo 
    private String myBeanName;
    //...

将产生 json:


    "my_bean_name": "Sth"
    //...

【讨论】:

我尝试了这种方法,但收到一个错误:-- HttpMessageNotWritableException:无法写入内容:类型 com.fasterxml.jackson.databind.PropertyNamingStrategy$SnakeCaseStrategy 不存在(通过引用链:java.util.数组列表[0]); -- 这可能是因为我返回 List,但我不知道如何解决这个问题。 您可以通过更改 application.properties 来全局执行此操作。只需添加“spring.jackson.property-naming-strategy=UPPER_CAMEL_CASE”即可将所有内容设置为大写【参考方案2】:

其实有一个很简单的答案:

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() 
    Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
    b.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    return b;

我将它添加到我的主目录中,如下所示:

@SpringBootApplication
public class Application 
    public static void main(String [] args) 
        SpringApplication.run(Application.class, args);
    

    @Bean
    public Jackson2ObjectMapperBuilder jacksonBuilder() 
        Jackson2ObjectMapperBuilder b = new Jackson2ObjectMapperBuilder();
        b.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        return b;
    

【讨论】:

CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES 自 2.7 起已弃用,请改用 SNAKE_CASE。【参考方案3】:

映射器有一个用于PropertyNamingStrategy的setter(用于设置自定义属性命名策略的方法。)

在 tes 示例中看看它是如何工作的:

    @Test
    public void namingStrategy() throws Exception 
        final ObjectMapper mapper = new ObjectMapper();
        mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.PropertyNamingStrategyBase() 
            @Override
            public String translate(String s) 
                return s.toUpperCase();
            
        );

        final String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(new SomePojo("uuid_1", "user_1", "Bruce", "W.", 51));
        System.out.println(json);
    

    public static class SomePojo 
        private String someIdAttachedToIt;
        private String username;
        private String fistName;
        private String lastName;
        private int age;

        public SomePojo(String someIdAttachedToIt, String username, String fistName, String lastName, int age) 
            this.someIdAttachedToIt = someIdAttachedToIt;
            this.username = username;
            this.fistName = fistName;
            this.lastName = lastName;
            this.age = age;
        

        public String getSomeIdAttachedToIt() 
            return someIdAttachedToIt;
        

        public String getUsername() 
            return username;
        

        public String getFistName() 
            return fistName;
        

        public String getLastName() 
            return lastName;
        

        public int getAge() 
            return age;
        
    

输出:


  "SOMEIDATTACHEDTOIT" : "uuid_1",
  "USERNAME" : "user_1",
  "FISTNAME" : "Bruce",
  "LASTNAME" : "W.",
  "AGE" : 51


提供的策略(我使用 LOWERCASE 作为示例)

PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
PropertyNamingStrategy.SNAKE_CASE

要在 Spring 中全局添加策略,您至少可以通过以下两种方式进行:

将映射器模块声明为包含命名策略的 bean 根据需要配置自定义对象映射器

短版:

@Configuration
    public static class Config 
        @Bean
        public Module module() 
            return new SimpleModule() 
                @Override
                protected SimpleModule setNamingStrategy(PropertyNamingStrategy naming) 
                    super.setNamingStrategy(new PropertyNamingStrategy.PropertyNamingStrategyBase() 
                        @Override
                        public String translate(String propertyName) 
                            // example: "propertyName" -> "PROPERTYNAME"
                            return propertyName.toUpperCase();
                        
                    );
                    return this;
                
            ;
        
    

加长版:

为jackson模块声明bean:

// config auto scan by spring
@Configuration
public static class ConfigurationClass 

    // declare the module as a bean
    @Bean
    public Module myJsonModule() 
        return new MySimpleModule();
    


// jackson mapper module to customize mapping
private static class MySimpleModule extends SimpleModule 
    @Override
    protected SimpleModule setNamingStrategy(PropertyNamingStrategy naming) 
        return super.setNamingStrategy(new MyNameStrategy());
    


// your naming strategy
private static class MyNameStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase 
    @Override
    public String translate(String propertyName) 
        return propertyName.toUpperCase();
    


您也可以在 xml 中声明 bean。

它不会覆盖明确定义道具名称的@JsonProperty。

【讨论】:

我应该对 Confid 类做些什么吗?把它挂在某个地方? 是的,只需在 Spring 可以在启动时自动扫描的包中创建它。它只是持有bean声明。您也可以使用 xml 配置创建它(需要创建自己的 SimpleModule 扩展类)。

以上是关于如何使用 Jackson 全局定义命名约定的主要内容,如果未能解决你的问题,请参考以下文章

将JSON转换为Java Object,如何使用Jackson解析BadgerFish约定

如何使用 Jackson 处理命名空间重复的 xml 标签

您如何全局设置 Jackson 以忽略 Spring 中的未知属性?

如何在 Powershell 中使用文件命名约定获取文件

如何从杰克逊 XML 解析中删除命名空间定义

如何验证XML架构中的命名约定