2016-08-03 17 views
0

Ich habe mit Jackson-Datenformat-CSV zu kämpfen, um es CSV-Header zu machen.Auto CSV Header mit Spring MVC und jackson-dataformat-csv

Momentan kann ich Sammlungen von Entitäten (Liste) ausgeben, aber die Header sind nicht da, wodurch die Datei nicht analysiert werden kann, weil die Spalten keinen Titel haben.

Meine MVC config (verkürzt):

@Configuration 
@EnableWebMvc 
public class MvcConfig extends WebMvcConfigurerAdapter { 

    @Autowired 
    private Environment env; 

    public static final String JSON_OBJECT_MAPPER_NAME = "json"; 
    public static final String CSV_OBJECT_MAPPER_NAME = "csv"; 

    private static final TimeZone OUTPUT_DATE_TIMEZONE = TimeZone.getTimeZone(ZoneOffset.UTC); 
    private static final DateFormat OUTPUT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 

    @Autowired 
    @Qualifier(value = JSON_OBJECT_MAPPER_NAME) 
    private ObjectMapper jsonObjectMapper; 

    @Autowired 
    @Qualifier(value = CSV_OBJECT_MAPPER_NAME) 
    private ObjectMapper csvObjectMapper; 

    public MvcConfig() { 
     super(); 
    } 



    @Override 
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { 
     configurer.favorPathExtension(true); 
     configurer.ignoreAcceptHeader(false); 
     configurer.defaultContentType(MediaType.APPLICATION_JSON); 
     configurer.useJaf(false); 

     final Map<String,MediaType> mediaTypes = new HashMap<>(); 
     mediaTypes.put("html", MediaType.TEXT_HTML); 
     mediaTypes.put("json", MediaType.APPLICATION_JSON); 
     mediaTypes.put("csv", new MediaType("text","csv", Charset.forName("utf-8"))); 
     configurer.mediaTypes(mediaTypes); 
    } 




    @Bean(name = JSON_OBJECT_MAPPER_NAME) 
    @Primary 
    public ObjectMapper jsonObjectMapper(){ 
     Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); 
     builder.indentOutput(Boolean.parseBoolean(env.getProperty("jsonPrettyPrint"))); 
     builder.timeZone(OUTPUT_DATE_TIMEZONE); 
     builder.dateFormat(OUTPUT_DATE_FORMAT); 
     return builder.build(); 
    } 

    @Bean(name = CSV_OBJECT_MAPPER_NAME) 
    public ObjectMapper csvObjectMapper(){ 
     CsvMapper csvMapper = new CsvMapper(); 

     //csvMapper.enable(CsvParser.Feature.WRAP_AS_ARRAY); 

     csvMapper.setTimeZone(OUTPUT_DATE_TIMEZONE); 
     csvMapper.setDateFormat(OUTPUT_DATE_FORMAT); 

     csvMapper.registerModule(new CsvMappingModule()); 
     csvMapper.registerModule(new JodaModule()); 

     return csvMapper; 
    } 



    @Override 
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 
     converters.add(createJsonHttpMessageConverter()); 
     converters.add(createCsvHttpMessageConverter()); 
    } 

    private HttpMessageConverter<Object> createJsonHttpMessageConverter() { 
     return createHttpMessageConverter(jsonObjectMapper, MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8); 
    } 

    private HttpMessageConverter<Object> createCsvHttpMessageConverter() { 
     return createHttpMessageConverter(csvObjectMapper, TEXT_CSV); 
    } 

    private HttpMessageConverter<Object> createHttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes){ 
     MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper); 
     converter.setSupportedMediaTypes(Lists.newArrayList(supportedMediaTypes)); 
     return converter; 
    } 
} 

Ein Controller, der eine Liste von Werten ouput:

@Controller 
@RequestMapping("/api/history") 
public class HistoricController { 


    @Autowired 
    public IHistoryService historyService; 
    @Autowired 
    public IThingService thingService; 

    @RequestMapping(value = "/{id}", method = RequestMethod.GET) 
    @ResponseBody 
    public List<HistoryDTO> findHistory(@PathVariable("id") Long thingId){ 
     Thing thing = thingService.find(thingId); 
     return historyService.findByThing(thing); 
    } 

} 

ich in der Lage bin zurück im JSON-Format:

[ 
    { 
     "location": { 
     "id": 101483, 
     "name": "City A" 
     }, 
     "dateEnteredLocation": "2016-06-06T18:44:03.000Z", 
     "dateLeavingLocation": "2016-06-13T13:02:34.000Z" 
    }, 
    { 
     "location": { 
     "id": 101483, 
     "name": "City A" 
     }, 
     "dateEnteredLocation": "2016-06-13T16:02:34.000Z", 
     "dateLeavingLocation": "2016-06-15T11:54:57.000Z" 
    }, 
    { 
     "location": { 
     "id": 101485, 
     "name": "City C" 
     }, 
     "dateEnteredLocation": "2016-06-16T04:05:06.000Z", 
     "dateLeavingLocation": "2016-06-16T11:34:58.000Z" 
    } 
] 

Aber wenn ich versuche, das CSV-Format zu verwenden, ich erhalte:

2016-06-06T18:44:03.000Z,2016-06-13T13:02:34.000Z,101483,City A 
2016-06-13T16:02:34.000Z,2016-06-15T11:54:57.000ZZ,101483,City A 
2016-06-16T04:05:06.000Z,2016-06-16T11:34:58.000Z,101485,City C 

So das Format in CSV, es ist in Ordnung. Aber es sind keine Header enthalten. Ich brauche Header, um die Datei von Menschen oder Maschinen verständlich zu machen.

Wie kann ich Jacksonscsv-Mapper machen, um automatisch Überschriften einzuschließen. Die Header-Namen sollten die gleichen sein wie die für Json (die Entity @ Json ... Annotationen verwendet)?

Ich möchte es so allgemein wie möglich halten. Ich möchte keine MVC-Controller für CSV schreiben.

Antwort

0

Der Mechanismus Jackson verwendet, ist, dass CsvSchema Bedürfnisse verwendet header aktiviert haben, so etwas wie:

CsvMapper mapper = new CsvMapper(); CsvSchema schema = CsvSchema.emptySchema().withHeader(); String csv = mapper.writer(schema).writeValueAsString(...);

die Herausforderung dann ist das wie CsvSchema passieren zu verwenden; oder möglich CsvWriter konfiguriert, um eins zu verwenden. Ich bin mir nicht sicher, ob Spring MVC dies explizit unterstützt.

+0

Danke, ich endete mit CSV-Export im Front-End. Der Hauptgrund dafür ist, dass die meisten meiner Web-APIs tiefe JSON-Objekte zurückgeben, die nicht als CSV dargestellt werden können. – singe3

+0

@ singe3 Ja, tiefe Verschachtelung ist schwierig zu unterstützen. Jackson kann es irgendwie mit '@ JsonUnwrapped' unterstützen, aber das kann schnell unhandlich werden, so dass ein tatsächlicher Strukturwandel sinnvoll sein kann. – StaxMan