在 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的主要内容,如果未能解决你的问题,请参考以下文章