如何将 Spring 控制器从 POJO 转换为 return CSV?

How to make Spring Controller to return CSV from a POJO?

给定一个简单的 Java 对象:

public class Pojo {
    private String x;
    private String y;
    private String z;

    //... getters/setters ...
}

是否有一些我可以放在我的项目中的库来制作这样的控制器:

@RequestMapping(value="/csv", method=RequestMethod.GET, produces= MediaType.TEXT_PLAIN)
@ResponseBody
public List<Pojo> csv() {
    //Some code to get a list of Pojo objects
    //...

    return myListOfPojos;
}

要生成我的 Pojos 的 csv 文件?对于 Json 结果,我使用 Jackson 库。我需要另一个库来获取 CSV 结果。

作为一个简单的变体。您可以通过任何您想要的方式生成 csv,并且 return 它作为字符串。

像这样:

@RequestMapping(value="/csv", method=RequestMethod.GET)
@ResponseBody
public String csv() { 
    //Some code to get a list of Pojo objects
    //...
    StringBuilder sb = new StringBuilder();
    for (Pojo pojo: myListOfPojos){
        sb.append(pojo.getX());
        sb.append(",");
        sb.append(pojo.getY());
        sb.append(",");
        sb.append(pojo.getZ());
        sb.append("\n");
    }
    return sb.toString;
}

应该可以。

通过反射自动生成这个字符串看起来也很简单。

根据另一个问题,我为 Tsv 响应做了自己的 HTTPMessageConverter。

TsvMessageConverter.java

public class TsvMessageConverter extends AbstractHttpMessageConverter<TsvResponse> {

    public static final MediaType MEDIA_TYPE = new MediaType("text", "tsv", Charset.forName("utf-8"));
    private static final Logger logger = LoggerFactory.getLogger(TsvMessageConverter.class);

    public TsvMessageConverter() {
        super(MEDIA_TYPE);
    }

    protected boolean supports(Class<?> clazz) {
        return TsvResponse.class.equals(clazz);
    }

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

    protected void writeInternal(TsvResponse tsvResponse, HttpOutputMessage output) throws IOException, HttpMessageNotWritableException {
        output.getHeaders().setContentType(MEDIA_TYPE);
        output.getHeaders().set("Content-Disposition", "attachment; filename=\"" + tsvResponse.getFilename() + "\"");
        final OutputStream out = output.getBody();

        writeColumnTitles(tsvResponse, out);

        if (tsvResponse.getRecords() != null && tsvResponse.getRecords().size() != 0) {
            writeRecords(tsvResponse, out);
        }

        out.close();
    }

    private void writeRecords(TsvResponse response, OutputStream out) throws IOException {
        List<String> getters = getObjectGetters(response);
        for (final Object record : response.getRecords()) {
            for (String getter : getters) {
                try {
                    Method method = ReflectionUtils.findMethod(record.getClass(), getter);
                    out.write(method.invoke(record).toString().getBytes(Charset.forName("utf-8")));
                    out.write('\t');
                } catch (IllegalAccessException | InvocationTargetException e) {
                    logger.error("Erro ao transformar em CSV", e);
                }
            }
            out.write('\n');
        }
    }

    private List<String> getObjectGetters(TsvResponse response) {
        List<String> getters = new ArrayList<>();
        for (Method method : ReflectionUtils.getAllDeclaredMethods(response.getRecords().get(0).getClass())) {
            String methodName = method.getName();
            if (methodName.startsWith("get") && !methodName.equals("getClass")) {
                getters.add(methodName);
            }
        }
        sort(getters);
        return getters;
    }

    private void writeColumnTitles(TsvResponse response, OutputStream out) throws IOException {
        for (String columnTitle : response.getColumnTitles()) {
            out.write(columnTitle.getBytes());
            out.write('\t');
        }
        out.write('\n');
    }
}

TsvResponse.java

public class TsvResponse {
   private final String filename;
   private final List records;
    private final String[] columnTitles;

   public TsvResponse(List records, String filename, String ... columnTitles) {
       this.records = records;
       this.filename = filename;
       this.columnTitles = columnTitles;
   }
   public String getFilename() {
       return filename;
   }
   public List getRecords() {
       return records;
   }

    public String[] getColumnTitles() {
        return columnTitles;
    }
}

并在 SpringContext.xml 上添加以下内容:

<mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="com.mypackage.TsvMessageConverter"/>
        </mvc:message-converters>
    </mvc:annotation-driven>

因此,您可以像这样在控制器上使用:

@RequestMapping(value="/tsv", method= RequestMethod.GET, produces = "text/tsv")
    @ResponseBody
    public TsvResponse tsv() {
        return new TsvResponse(myListOfPojos, "fileName.tsv",
                "Name", "Email", "Phone", "Mobile");
    }