find-bug shows String can't cast to util.Map
When run through the application getting correct result.
siteList looks [{site_id=47, site_code=a}, {site_id=48, site_code=ABC}, {site_id=49, site_code=ABCD}, {site_id=54, site_code=ABCE}, {site_id=1, site_code=FXGL}]
public static List<SitesDto> transformSites(List<String> siteList) {
List<SitesDto> sitList = new ArrayList<>();
for (Object object : siteList) {
Map row = (Map) object;----->showing error
final SitesDto site = new SitesDto();
String code = (String) row.get(SITE_CODE);
Object id = row.get(SITE_ID);
site.setSiteId(((Number) id).longValue());
site.setSiteCode(code);
sitList.add(site);
}
return sitList;
}
find-bug shows String can't cast to util.Map
The java.util.Map interface represents a mapping between a key and a value.
Where as, siteList is a collection of Strings (they are not key and value pairs which Map is expecting), so is the error String can't cast to util.Map.
The List passed in parameter should be of List<Map<String,Object>>
Hope it helps.
You declare a method that accepts a List<String> argument. Since you say that the application runs without any apparent error, it would appear that the siteList argument is not actually a List<String> at all, but instead is a List<Map>. You should have got a findbugs warning somewhere else, probably in the method that calls transformSites, saying that you shouldn't be using raw types or that a List<Map> shouldn't be passed to a method that takes a List<String> argument. You should fix that first. You should also avoid using raw types: use List<whatever> instead of just List, and Map<String, Number> instead of just Map. Otherwise, you are making it easier to make errors.
Related
This question already has answers here:
What is a raw type and why shouldn't we use it?
(16 answers)
Closed 3 years ago.
Suppose a HashMap is defined as:
void defineMap(){
Map options = new HashMap<Object,Object>();
options.put("isValid","true");
options.put("isXYZ","false");
options.put("Size",6.0);
int x = getFromMap(options);
}
Now, when this map is passed to a function and in the signature of the function, the Map is defined as
static int getFromMap(Map<String, String> options) {
String some_number = "Size";
int val=Integer.parseInt(options.get(some_number));
return val;
}
Now, as per my understanding 1 of 2 things should have happened:
Either the java compiler should have thrown an error that no such method found with the same signature since map definition is different
All the keys and values defined in the Map should have been converted to String implicitly
But none of the above happens as a result of which in my Map, Double(6.0) value is stored with String ("Size") key, which results in ClassCastException as:
java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
in my getFromMap() function.
Help me understand why no error was thrown while calling the function and passing Map as a parameter or why values were not converted into String.
Why Double value was stored in <String,String> Map
This issue can be easily fixed by type checking for value or converting to string every time. But I want to understand why the above scenario occurred
Answering your Q. Why Double value was stored in Map
You have created Map
so you can put any datatype object in this map when you put only 6.0 it will behave as double it you put this as "6.0" its class type is String
like demo
Object obj="6.0";
System.out.println("type is ="+obj.getClass());
obj=6.0;
System.out.println("type is ="+obj.getClass());
obj=6;
System.out.println("type is ="+obj.getClass());
output:
type is =class java.lang.String
type is =class java.lang.Double
type is =class java.lang.Integer
so better to use Object return type.
Try use the Double.intValue();
https://www.tutorialspoint.com/java/lang/double_intvalue.htm
int val= options.get(some_number).intValue();
The double wasnt stored in a <String,String> Map, you get the Exception because Integer.parseInt expectes a String and that where the ClassCastException is thrown
Running my code results in the following error: java.lang.Integer cannot be cast to java.lang.String
This is what I have:
for(Map<String, Object> record: list) {
if(((String)record.get("level")).equals("1")) {
rootList.add(record);
}
}
I have tried converting that little snippet to an Int using toString but it just gives me a compiling error. I'm new to java (and I did not write this code either) so forgive me if it's a stupid mistake.
Your Maps holds values of type Object, meaning they can really hold anything as values (since everything extends Object); there's no guarantee that a given value will be a String. By your error, it would appear that one of the values in one of your maps is an Integer, which evidently cannot be cast to a String. The quick-fix is to use toString:
record.get("level").toString().equals("1")
But note that this will yield true for any object whose toString returns "1", not just strings. If you want to only check for equality with the string "1", then you can use "1".equals(...), since equals can take any object as an argument.
try
Object number = record.get("level");
if("1".equals(number)){
}
try one of this alternatives:
rootList.add(record + "");
or
rootList.add(new String(record));
Well, if the Map values are Integers, then toString() should work for converting them to strings:
for(Map<String, Object> record: list) {
if(record.get("level").toString().equals("1")) {
rootList.add(record);
}
}
I have a HashMap as below:
public static HashMap submitService(HashMap inputMap) throws Exception
{
//code....
ArrayList<String> allIdsList = (ArrayList<String>) inputMap.get("Result_List");
// System.out.println("Result List: " + allIdsList); prints the arraylist (e.g. [2, 21, 6, 3]
for(int i=0;i<allIdsList.size();i++)
{
System.out.println(" values: " + (String)allIdsList.get(i));
}
}
the arraylist is printing in the console(I tried it only to see if the list is not empty). But for (String)allIdsList.get(i) inside the for loop following error message is coming
java.lang.Long cannot be cast to java.lang.String
Would really appreciate someone's help.
Make sure HashMap is of type HashMap<String,ArrayList<String>>
Replace: public static submitService(HashMap<'String,ArrayList<'String>> inputMap) throws Exception
Looking at the exception, its sure that the parameter passed does not have arraylist of string.Looks like it might be ArrayList<'Object>(with long value present in the list which eventually is casted in String) or Arraylist<'Long>.
Try this:
public static HashMap<String,ArrayList<String>> submitService(HashMap<String,ArrayList<String>> inputMap) throws Exception
{
//code....
ArrayList<String> allIdsList = inputMap.get("Result_List");
for(int i=0;i<allIdsList.size();i++)
{
System.out.println(" values: " + allIdsList.get(i));
}
}
Where is this inputMap coming from? By the looks of it, its values are not of type ArrayList<String> but something like ArrayList<Object> or even ArrayList<Long>. The problem seems to be somewhere outside your posted snippet.
Since you're working with raw types in this method, the compiler won't complain (although it should warn you about using raw types). However, the VM will throw a ClassCastException where the cast fails - that's what you're seeing.
Try strengthening your signature by only accepting HashMap<String, ArrayList<String>> as inputMap. That way, you can get rid of those awful casts, as you now get compile time type checks. In the best case, this will give the compiler enough information to point out where you're calling this method with an incorrectly typed inputMap. You should then be able to easily fix the error merely by following the compiler's instructions. If that doesn't work, you're probably using raw types in your calls as well and you'll need to dig down the stack trace to fix those.
I want to parse some Strings (they include some numbers) with same format but different number kind and set these number to their related array. for example having this string: " "positions":[[35.23,436.34],[23.5, 7.1]] I want to put these number into a float array named "this"! and for this string" "indices":[[23,4],[2,1]]" I want to put them into an integer array named "that"!
To do so, I've wrote a generic function with this declaration:
private <E extends Number> voidfunc(ArrayList<E> array, String JSON){
.
.
array.add((E) NumberFormat.getInstance().parse(JSON.substring(...)));
.
.
}
this works well and put numbers into array correctly but later, in somewhere in my app I get a "class cast exception. Can not cast Double to Float" trying to do this:
floatArray[i] = temp.get(i);
temp have defined as a float arraylist and have filled with above function.
can anybody tell me why it is so, and how can I solve that? I really appreciate that.
In your example, E is type parameter of generic method. This type is not known at run time, so cast to this type done with (E) is fake and compiler most probably reported warning at this line. JVM does not check type compatibility during this cast, so it is possible that value of incompatible type will get into the list. For example, value of Double type may be stored in List<Float>. Later, when you try to extract value form the list and cast it to Float, ClassCastException will occur.
You probably need to change your code like this:
private void func (ArrayList <? super Double> array, String JSON)
{
...
array.add (NumberFormat.getInstance ().parse (JSON.substring (...)));
...
}
ArrayList <Number> temp = new ArrayList ();
func (temp, json);
floatArray [i] = temp.get (i).floatValue ();
Try with:
floatArray[i] = ((Double) temp.get(i)).floatValue();
Attempting to de-serialize JSON to a Map<K, V> where K is a String and V one of several types (i.e String int or boolean). Using Gson, I tried the following code:
public static void main(String[] args) {
JsonObject json = new JsonObject();
json.addProperty("str", "str-value");
json.addProperty("int", 10);
json.addProperty("bool", true);
// "json" now contains {"str":"str-value","int":10,"bool":true}
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(json, mapType);
String str = (String) map.get("str"); // cannot cast Object to String
str = String.valueOf(map.get("str")); // results in java.lang.Object#1632847
}
Technical Answer
If you attach the Gson source code and then debug the program you will find the source of the problem. The problem seems to rest in the Gson code.
Since the value is of type Object your call to fromJson(), the class com.google.gson.ObjectNavigator will be used and comes into this section of code [~line 113]
else if (objTypePair.type == Object.class && isPrimitiveOrString(objectToVisit)) {
visitor.visitPrimitive(objectToVisit); //Target value is the proper value after this call
visitor.getTarget(); //target value is now a blank Object.
where visitor is of type JSonObjectDeserializationVisitor, objectToVisit is your value and objectTypePair is the type it is to become (Object).
Now, The JsonObjectDeserializationVisitor has a flag called constructed which is set to false by default. so when the getTarget() method is called, it checks to see if the constructed flag is true, if not it creates a new instance of the object you are trying to create. since the constructed flag is never set the call to getTarget() returns a blank Object.
The call to getTarget() seems unnecessary in the code and appears to be the source of the problem. visitPrimitive performs the proper action by placing the parsed value into the target variable which would be passed back. This appears to be a bug surrounding the Object type.
This isn't something you could fix unless you are willing to do a custom build of Gson. I would recommend filing this as a report on the Gson forums.
There is already one report filed for version 1.5
Work Around
I see two ways you can fix this in the short term.
1) Make the Map of type <String, String>, though you lose the types you can still get the values back properly
2) Put the values into a bean class first and then Serialize that.
public class myObj{
String myStr;
int myInt;
boolean myBool;
//get and set methods
}
public static void main(String args[]){
MyObj o = new MyObj();
o.setMyStr("str-value");
Gson gson = new Gson();
String json = gson.toJson(o);
o = gson.fromJson(json, MyObj.class);
}
Both defeat the purpose of what you are trying to do, but it is in googles court to fix this bug I believe.