question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Generics cause ClassCastException: java.util.LinkedHashMap cannot be cast

See original GitHub issue

Using the Jackson TypeReference object and Java Generics causes a ClassCastException with the ObjectMapper’s readValue method. Specifically, the following does not work:

new TypeReference<List<MyTimeSeriesResult<R>>>(){ }.

I have included the sample JUnit test class JacksonTypeReferenceGenericsTest, that highlights this issue.

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import org.junit.Test;
import junit.framework.Assert;



@SuppressWarnings( "nls" )
public class JacksonTypeReferenceGenericsTest
{
    private static final ObjectMapper MAPPER = new ObjectMapper()
            .configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );

    private static final String SAMPLE_JSON =
            "[\n" +
            "    {\n" +
            "        \"timestamp\" : \"2016-06-28T17:31:58.818+0000\",\n" +
            "        \"result\" : {\n" +
            "            \"temperature\" : 22.1,\n" +
            "            \"humidity\" : 64.1,\n" +
            "            \"wind\" : 19.1\n" +
            "        }\n" +
            "    }, {\n" +
            "        \"timestamp\" : \"2016-06-28T17:31:58.818+0000\",\n" +
            "        \"result\" : {\n" +
            "            \"temperature\" : 29.2,\n" +
            "            \"humidity\" : 40.2,\n" +
            "            \"wind\" : 10.2\n" +
            "        }\n" +
            "    }\n" +
            "]";

    @Test
    public void testWithoutGenerics() throws Exception
    {
        List<MyTimeSeriesResult<MyWeatherResult>> resultList = getResultWithoutGenerics( SAMPLE_JSON );

        Assert.assertEquals( MyTimeSeriesResult.class, resultList.get( 0 ).getClass() );
        Assert.assertEquals( MyTimeSeriesResult.class, resultList.get( 1 ).getClass() );

        Assert.assertEquals( MyWeatherResult.class, resultList.get( 0 ).getResult().getClass() );
        Assert.assertEquals( MyWeatherResult.class, resultList.get( 1 ).getResult().getClass() );
    }

    private List<MyTimeSeriesResult<MyWeatherResult>> getResultWithoutGenerics( String json ) throws Exception
    {

        List<MyTimeSeriesResult<MyWeatherResult>> resultList =
            MAPPER.readValue( json, new TypeReference<List<MyTimeSeriesResult<MyWeatherResult>>>()
            {
                // NO-OP
            } );
        return resultList;
    }

    @Test
    public void testGenerics() throws Exception
    {
        List<MyTimeSeriesResult<MyWeatherResult>> resultList = getResultUsingGenerics( SAMPLE_JSON );

        Assert.assertEquals( MyTimeSeriesResult.class, resultList.get( 0 ).getClass() );
        Assert.assertEquals( MyTimeSeriesResult.class, resultList.get( 1 ).getClass() );

        // WILL FAIL with following exception:
        //   java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to JacksonTypeReferenceGenericsTest$MyWeatherResult
        Assert.assertEquals( MyWeatherResult.class, resultList.get( 0 ).getResult().getClass() );
        Assert.assertEquals( MyWeatherResult.class, resultList.get( 1 ).getResult().getClass() );
    }

    private <R> List<MyTimeSeriesResult<R>> getResultUsingGenerics( String json ) throws Exception
    {

        List<MyTimeSeriesResult<R>> resultList =
            MAPPER.readValue( json, new TypeReference<List<MyTimeSeriesResult<R>>>()
            {
                // NO-OP
            } );
        return resultList;
    }

    @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY )
    @JsonIgnoreProperties( ignoreUnknown = true )
    @JsonPropertyOrder( { "timestamp", "result" } )
    public static class MyTimeSeriesResult<T>
    {
        private Date timestamp;
        private T result;

        public MyTimeSeriesResult()
        {
            //
        }

        public Date getTimestamp()
        {
            return timestamp;
        }

        public void setTimestamp( Date timestamp )
        {
            this.timestamp = timestamp;
        }

        public T getResult()
        {
            return result;
        }

        public void setResult( T result )
        {
            this.result = result;
        }

    }

    @JsonAutoDetect( fieldVisibility = JsonAutoDetect.Visibility.ANY )
    @JsonIgnoreProperties( ignoreUnknown = true )
    @JsonPropertyOrder( {
        "temperature", "humidity", "wind" } )
    public static class MyWeatherResult
    {
        private Double temperature;
        private Double humidity;
        private Double wind;

        public MyWeatherResult()
        {
            //
        }

        public Double getTemperature()
        {
            return temperature;
        }

        public void setTemperature( Double temperature )
        {
            this.temperature = temperature;
        }

        public Double getHumidity()
        {
            return humidity;
        }

        public void setHumidity( Double humidity )
        {
            this.humidity = humidity;
        }

        public Double getWind()
        {
            return wind;
        }

        public void setWind( Double wind )
        {
            this.wind = wind;
        }
    }

    public static void main( String [] args ) throws Exception
    {
        List<MyTimeSeriesResult<MyWeatherResult>> resultList = new ArrayList<>();

        final int hourToMillis = 1000 * 60 * 60;

        {
            MyWeatherResult myWeatherResult = new MyWeatherResult();
            myWeatherResult.setTemperature( 22.1 );
            myWeatherResult.setHumidity( 64.1 );
            myWeatherResult.setWind( 19.1 );
            MyTimeSeriesResult<MyWeatherResult> myTimeSeriesResult = new MyTimeSeriesResult<>();
            myTimeSeriesResult.setTimestamp( new Date( System.currentTimeMillis() - hourToMillis * 2 )  );
            myTimeSeriesResult.setResult( myWeatherResult );

            resultList.add( myTimeSeriesResult );
        }

        {
            MyWeatherResult myWeatherResult = new MyWeatherResult();
            myWeatherResult.setTemperature( 29.2 );
            myWeatherResult.setHumidity( 40.2 );
            myWeatherResult.setWind( 10.2 );
            MyTimeSeriesResult<MyWeatherResult> myTimeSeriesResult = new MyTimeSeriesResult<>();
            myTimeSeriesResult.setTimestamp( new Date( System.currentTimeMillis() - hourToMillis * 2 )  );
            myTimeSeriesResult.setResult( myWeatherResult );

            resultList.add( myTimeSeriesResult );
        }

        System.out.println( MAPPER.writeValueAsString( resultList )  );
    }

}

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

22reactions
himadripantcommented, Sep 15, 2017

@mark1900 below is what worked for me, slightly different from yours though:

    public <T> List<T> jsonToObjectList(String json, Class<T> tClass) throws IOException {
        ObjectMapper MAPPER = new ObjectMapper();
        List<T> ts = MAPPER.readValue(json, MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, tClass));
        return ts;
    }

I reckon it to be suited if you use CollectionType outer = typeFactory.constructCollectionType( ArrayList.class, inner ); rather than JavaType outer = typeFactory.constructParametricType( ArrayList.class, inner );

13reactions
cowtowncodercommented, Jun 28, 2016

This is a limitation of Java generics and specifically Type Erasure: while you may pass type variable R, it does not resolve to anything dynamically so this:

new TypeReference<List<MyTimeSeriesResult<R>>>

is essentially just same as

new TypeReference<List<MyTimeSeriesResult<?>>>
// or
new TypeReference<List<MyTimeSeriesResult<Object>>>

and as a result binding will bind contents as Map instead of whatever R is at compile time. Code that accesses results will however assume the type that was used (and for which R is expected to bind to), which is what gives cast exception.

If you need to dynamically pass arbitrary types instead of static types, you have to use TypeFactory and construct actual types passing Class as content type.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Jackson: java.util.LinkedHashMap cannot be cast to X
Learn why the "java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to X" exception occurs and how to solve the problem.
Read more >
java.util.LinkedHashMap cannot be cast to com.testing.models ...
I had a similar exception (but different problem) - java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to org.bson.
Read more >
java.util.LinkedHashMap cannot be cast to com.testing.FeedItem
The ClassCastException thrown to indicate that your code has attempted to cast an object to a subclass of which it is not an...
Read more >
How to solve "java.util.LinkedHashMap cannot be cast to java ...
This basically means that your Query is working fine, but the result cannot be displayed as a table - which is the default...
Read more >
java.util.hashmap cannot be cast to java.lang.string - You.com
The issue's coming from Jackson. When it doesn't have enough information on what class to deserialize to, it uses LinkedHashMap .
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found