Just few days ago I was implementing small feature on one of our minor projects . The idea process some data on user interface pass it to backend and send it further to third party service.
So on UI we have code like this
$.post( "/backendService", JSON.stringify(idList); );
Obviously idList contains plain long type id values e.g. 54396 so that on backendService we have following piece of code
@SuppressWarnings("unchecked") List<Long> idList= mapper.readValue(idListJson, List.class);
And later on I would like include that list as part of other structure
public class ThirdPartyServiceRequest { public List<Long> idList = new ArrayList<Long>(); ..........
so I did following
ThirdPartyServiceRequest request = new ThirdPartyServiceRequest(); request.idList.addAll(idList); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(request); executePOSTQuery("ThirdPartyServiceURL", json); ......
So everything pretty clear I wrote unit test where I manually created idList and so on, everything worked like a charm, but when I started testing the whole solution following line
String json = mapper.writeValueAsString(request);
threw me exception
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: java.lang.String cannot be cast to java.lang.Long (through reference chain: com.pack.ThirdPartyServiceRequest ["idList"]->java.util.ArrayList[0]) at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210) at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:189) at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:216) at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContentsUsing(IndexedListSerializer.java:142) at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:82) at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:73) at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:19) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129) at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3387) at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:2781) at com.pack.BackendService(BackendService.java:151) Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long at com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer.serialize(NumberSerializers.java:175) at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContentsUsing(IndexedListSerializer.java:136) ... 10 more
So what a ClassCastException from String to Long is mentioned if we do exactly opposite and convert List<Long> to String ?
I’m sure many watchful readers already figured out issue. I did a mistake in following line
@SuppressWarnings("unchecked") List<Long> idList = mapper.readValue(idListJson, List.class);
We cannot do it. If we check json we received from UI we will see following line
JSON.stringify(idList)
returns such json
"["26512","26515"]"
so that when we use jackson to deserialize we should be careful and use proper generic for list
@SuppressWarnings("unchecked") List<String> idList= mapper.readValue(idListJson, List.class);
or provide proper type
List<Long> idList = mapper.readValue(idListJson, new TypeReference<list<long>>() {})
It’s pure developer faults and it’s quite easy to do such mistake so remember about it
UPD.
Thx to comments MVMn the proper fix would change not server side but UI part
So before writing to idList you have check if it’s really int and not a String and convert it to int on UI if needed with function parseInt
In my case I receive id as value of checkboxes and obviously it is String not int