在 Spring Boot 中自动将 Rest 控制器序列化为 CSV 而不是 JSON

Posted

技术标签:

【中文标题】在 Spring Boot 中自动将 Rest 控制器序列化为 CSV 而不是 JSON【英文标题】:Automatically serialize Rest controllers to CSV instead of JSON in Spring Boot 【发布时间】:2019-04-09 23:22:55 【问题描述】:

我正在寻找一种快速/简单的解决方案,如何将 Rest 控制器输出自动序列化为 CSV 而不是 JSON。我有最简单的 Spring boot 应用程序:

@SpringBootApplication
public class CsvExportApplication 

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


class User 
    String name;
    String surname;

    public User(String name, String surname) 
        this.name = name;
        this.surname = surname;
    

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

    public void setSurname(String surname) 
        this.surname = surname;
    

    public String getName() 
        return name;
    

    public String getSurname() 
        return surname;
    


@RestController
class UserController 
    @GetMapping(value = "/users")
    List<User> list() 
        return Arrays.asList(new User("adam", "kowalsky"), new User("john", "smith"));
    

我使用了jackson-dataformat-csv 并想出了以下将List&lt;User&gt; 序列化为String 的代码,但理想情况下我不想更改其余控制器代码:

CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(User.class).withHeader();
mapper.writerFor(List.class).with(schema).writeValueAsString(users);

理想情况下,我希望我的控制器能够根据请求中的 Accept 标头以 JSON 或 CSV 格式返回输出。

【问题讨论】:

希望这个答案可以帮助您正确定义您的休息控制器的返回格式***.com/a/33417769/8553816 【参考方案1】:

我设法通过以下方式实现我想要的:

为 application/csv 定义自定义转换器 转换器只能写入CSV(不支持读取) 转换器使用 Jackon 的 ObjectMapper(以确保 CSV 和 JSON 输出使用例如相同的日期格式) 转换器即时构建jackson-dateformat-csv 模式,因为当前不支持无模式写入:https://github.com/FasterXML/jackson-dataformats-text/issues/114

代码:

class CsvConverter<T> extends AbstractHttpMessageConverter<T> 

    private final ObjectMapper objectMapper;

    CsvConverter(ObjectMapper objectMapper) 
        super(new MediaType("application", "csv"));
        this.objectMapper = objectMapper;
    

    @Override
    protected boolean supports(Class<?> clazz) 
        return true;
    

    @Override
    protected T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException 
        return null;
    

    @Override
    protected void writeInternal(T object, HttpOutputMessage outputMessage) 
           throws IOException, HttpMessageNotWritableException 
        try 
            ObjectWriter objectWriter = getCsvWriter(object);
            try (PrintWriter outputWriter = new PrintWriter(outputMessage.getBody())) 
                outputWriter.write(objectWriter.writeValueAsString(object));
            
         catch (Exception e) 
            throw new RuntimeException(e);
        
    

    ObjectWriter getCsvWriter(T object) 
        Set<String> fields = getUniqueFieldNames(object);
        CsvSchema.Builder schemaBuilder = CsvSchema.builder().setUseHeader(true);
        for (String field : fields) 
            schemaBuilder.addColumn(field);
        
        return new CsvMapper().writerFor(List.class).with(schemaBuilder.build());
    

    Set<String> getUniqueFieldNames(T object) 
        try 
            JsonNode root = objectMapper.readTree(objectMapper.writeValueAsString(object));
            Set<String> uniqueFieldNames = new LinkedHashSet<>();
            root.forEach(element -> 
                Iterator<String> it = element.fieldNames();
                while (it.hasNext()) 
                    String field = it.next();
                    uniqueFieldNames.add(field);
                
            );
            return uniqueFieldNames;
         catch (IOException ex) 
            throw new RuntimeException(ex);
        
    


@Configuration
class AppConfig implements WebMvcConfigurer 

    private final ObjectMapper objectMapper;

    AppConfig(ObjectMapper objectMapper) 
        this.objectMapper = objectMapper;
    

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) 
        converters.add(new CsvConverter<>(objectMapper));
    

【讨论】:

以上是关于在 Spring Boot 中自动将 Rest 控制器序列化为 CSV 而不是 JSON的主要内容,如果未能解决你的问题,请参考以下文章

初入spring boot(八 )Spring Data REST

无法使用 Spring Boot 自动配置访问 REST 控制器

Spring Boot REST API 自动化测试

无法使用 Spring Boot Rest 服务将 JSON 转换为 Java 对象

Spring Boot 的 thymeleaf 自动配置问题试图解决 rest 应用程序的错误模板

使用spring-boot对rest服务进行访问控制