I have an Object (here: Property) and I want to add csv export ability to my Spring Backend for single and list of objects.
I added this to my config:
@Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.(more config).mediaType("csv", new MediaType("text", "csv")); } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new PropertyConverter()); converters.add(new StatsConverter()); }
and the Property Converter looks like this:
public class PropertyConverter extends AbstractGenericHttpMessageConverter<Property> { private static final Logger LOGGER = LoggerFactory.getLogger(PropertyConverter.class); public PropertyConverter() { super(new MediaType("text", "csv")); } @Override protected void writeInternal(Property property, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { try (var writer = new OutputStreamWriter(outputMessage.getBody())) { new StatefulBeanToCsvBuilder<>(writer).withSeparator(',').build().write(property); } catch (CsvDataTypeMismatchException | CsvRequiredFieldEmptyException ex) { LOGGER.error("CSV failed to convert property: ".concat(property.getExternalId()).concat(", exception: ".concat(ex.toString()))); } } @Override protected Property readInternal(Class<? extends Property> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return null; } @Override public Property read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return null; } }
This code works for a Single Property. When I try to return a list of Properties:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class eu.webeng.model.Property (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; eu.webeng.model.Property is in unnamed module of loader 'app') ... Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class eu.webeng.model.Property (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; eu.webeng.model.Property is in unnamed module of loader 'app') at eu.webeng.converter.PropertyConverter.writeInternal(PropertyConverter.java:20) ~[classes/:na] at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:104) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:287) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:219) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE] ....
I tried to add a PropertyListConverter but then it doesn’t work for Single Property. When I tried to add both, the first converter added is being used.
How can I make the Converter work for Single and List of Property (or any Object)
Advertisement
Answer
Does it work if you try adding the following method to your (single-property) converter?
@Override public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) { return super.canWrite(clazz, mediaType) && clazz == Property.class; }
The AbstractMessageConverterMethodProcessor
class loops through all registered converters and skips any for which the canWrite
method returns false
. I’ve adapted the method above from the AbstractGenericHttpMessageConverter
class, adding an extra check on the class of the value to be written, so that it should only be used to write out Property
values.
Note that the single-property converter should be added to the list of converters before the multi-property converter. Alternatively, make a similar modification to your multi-property converter too.