Unable to deserialize map - cannot convert HashMap to class exception

3,805 views
Skip to first unread message

Tim R

unread,
Sep 8, 2016, 2:24:04 AM9/8/16
to REST assured
I get the following exception when trying to deserialize a map

java.lang.ClassCastException: Cannot convert class java.util.HashMap to class com.my.mag.model.Customer.

code:
extract(). response().getBody().jsonPath() .getMap("$", String.class, Customer.class);

I saw a similar solution suggestion in: Deserialize Arbitrary String Key
Have you tried Map map = get("/x").as(Map.class) or Map<String, ValueClassObj> map = get("/x").jsonPath().getMap("$", String.class, ValueClassObj.class)?

I am using maven and have fasterxml core/databind dependencies
For a test, I stored the json to a file and ran this, works fine: (fasterxml only)
        Map<String, Customer> map = mapper.readValue(FileUtils.readFileToByteArray(file),
                new TypeReference<Map<String, Customer>>() {
                });
This also works: (fasterxml, jaxrs)
map = response.readEntity(new GenericType<Map<String, Customer>>() {  });

I just switched to 3.01, from 3.0, but I am getting the same error.

It seems like its not using Jackson, but the mapper.readValue() test is running in the same file.

Any thoughts on how on how to resolve this would be greatly appreciated,
Tim


the response json  (Map<String, Customer>) body:

{
    "24": {
        "entity_id": "24",
        "website_id": "1",
        "email": "jack-at-example-dot-com",
        "group_id": "2",
        "created_at": "2013-03-28 18:57:17",
        "disable_auto_group_change": "0",
        "created_in": "Admin",
        "prefix": null,
        "firstname": "Jack",
        "lastname": "Fitz",
        "suffix": null,
        "taxvat": null,
        "dob": "2001-01-03 00:00:00",
        "gender": "1",
        "reward_update_notification": "1",
        "reward_warning_notification": "1"
    }
}

and class Customer.java
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Customer {

    String email;
    String firstname;
    String lastname;
    String entity_id;
    @JsonIgnore
    String password;

    public Customer() {
    };
// getter/setters ...
}



 

Tim R

unread,
Sep 9, 2016, 12:19:46 AM9/9/16
to REST assured
If I change my value from a Map<String, Customer> map = ... to  Customer customer = ... and change the GPATH
response().getBody().jsonPath().getObject("24", Customer.class);  
I get back the Customer object  filled in correctly, great. Unfortunately, I don't know what the key:24 is in the general case,
i would need a wild card.

Tim R

unread,
Sep 9, 2016, 2:46:21 AM9/9/16
to REST assured

a more complete example, but with hard coded  "24.email" it is not a general solution
the gpath has got me stumped.

@Test
    public void getCustomerByEmail() throws Exception {
        String emailFilter = "filter[1][attribute]=email&filter[1][in][0]=";
        RestAssured.urlEncodingEnabled = false;

        given().
            auth().
            oauth(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, SECRET_TOKEN, OAuthSignature.HEADER).
        when().
            get("/customers/?" + emailFilter + existingCustomer.getEmail()).
        then().
            statusCode(200).
            assertThat().
            body("24.email", equalTo(existingCustomer.getEmail()));

Johan Haleby

unread,
Sep 23, 2016, 1:15:02 PM9/23/16
to rest-a...@googlegroups.com
Hi Tim, 

Yes I believe this to be a limitation of RestAssured's JsonPath library. You could file an issue if you like. Sorry for the late response.

Regards,
/Johan

--
You received this message because you are subscribed to the Google Groups "REST assured" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rest-assured+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tim R

unread,
Nov 22, 2016, 9:53:02 PM11/22/16
to REST assured
Johan,

I didn't see this reply until just now.
I have done a little more testing, my path was probably wrong "$".
I went to jsonpath tester and found that if I used  "$.*" i could get back and array
but rest-assured doesn't like "$.*"

[  
   {  

      "entity_id":"24",
      "website_id":"1",
      "email":"ja...@example.com",

      "group_id":"2",
      "created_at":"2013-03-28 18:57:17",
      "disable_auto_group_change":"0",
      "firstname":"Jack",
      "lastname":"Fitz",
      "created_in":"Admin",
      "prefix":null,
      "suffix":null,
      "taxvat":null,
      "dob":"2001-01-03 00:00:00",
      "reward_update_notification":"1",
      "reward_warning_notification":"1",
      "gender":"1"
   }
]
                                       
                                       
                               

error for path = "$.*"

Enter code here...FAILED: getCustomerByEmailB
java
.lang.IllegalArgumentException: Invalid JSON expression:
Script1.groovy: 1: unexpected token: * @ line 1, column 29.
                            $
.*
                               
^

To unsubscribe from this group and stop receiving emails from it, send an email to rest-assured...@googlegroups.com.

Johan Haleby

unread,
Nov 23, 2016, 1:46:17 AM11/23/16
to rest-a...@googlegroups.com
On Wed, Nov 23, 2016 at 3:53 AM, Tim R <tim...@gmail.com> wrote:
Johan,

I didn't see this reply until just now.
I have done a little more testing, my path was probably wrong "$".
I went to jsonpath tester and found that if I used  "$.*" i could get back and array
but rest-assured doesn't like "$.*"

True, but these are two different projects (what they share is the same name unfortunately) with different syntax. The first uses the json path "specification" and the other (Rest Asssured JsonPath) uses GPath.
 
To unsubscribe from this group and stop receiving emails from it, send an email to rest-assured+unsubscribe@googlegroups.com.

Tim R

unread,
Nov 28, 2016, 2:44:59 AM11/28/16
to REST assured
Johan,

I understand the two testers use two different types of jsonpath - GPath and Rest-Assurred JsonPath.
I totally agree - its extremely confusing to use the same name for both ...

My point was if "$.*" works on both testers it should work with rest-assured regardless of which of the two JsonPath libs
are used. Am I correct with this assumption?

From your first answer above, it sounds like trying to grab the map of  <String, Customer> directly from the root - is either a bug or was never implemented in RA.
I will enter an issue as you suggested.

In the end, I'm just looking for a way to get the Customer data out of the response.
In Jersey I use this:
Map<String, Customer> map =  response.readEntity(new GenericType<Map<String, Customer>>() { });

Can you suggest a correct "path" string that will get me the data I'm looking for?

Thanks,
Tim

Johan Haleby

unread,
Nov 28, 2016, 7:33:06 AM11/28/16
to rest-a...@googlegroups.com
On Mon, Nov 28, 2016 at 8:44 AM, Tim R <tim...@gmail.com> wrote:
Johan,

I understand the two testers use two different types of jsonpath - GPath and Rest-Assurred JsonPath.
I totally agree - its extremely confusing to use the same name for both ...

I think you've missunderstood. REST Assured includes a library called json-path (group id io.rest-assurued) which uses the GPath syntax. Then there's a Jayway library called jsonpath which uses the "json path" syntax and this is totally different from rest-assured. These two have just two things in common, the names are confusingly similar and they can extract values from a json document (with different syntax).
 

My point was if "$.*" works on both testers it should work with rest-assured regardless of which of the two JsonPath libs
are used. Am I correct with this assumption?

No
 

From your first answer above, it sounds like trying to grab the map of  <String, Customer> directly from the root - is either a bug or was never implemented in RA.
I will enter an issue as you suggested.

It's not a bug in RA, I would think it's a feature suggestion to the groovy-json module (JsonSlurper).
 

In the end, I'm just looking for a way to get the Customer data out of the response.
In Jersey I use this:
Map<String, Customer> map =  response.readEntity(new GenericType<Map<String, Customer>>() { });

Can you suggest a correct "path" string that will get me the data I'm looking for?

Hmm maybe you can do like this (with REST Assured json path):

JsonPath jsonPath = JsonPath.from(<json>)
Map<String, Customer> customers = jsonPath.getMap("$", String.class, Customer.class)
 
To unsubscribe from this group and stop receiving emails from it, send an email to rest-assured+unsubscribe@googlegroups.com.

Tim R

unread,
Nov 28, 2016, 5:15:11 PM11/28/16
to REST assured




On Monday, November 28, 2016 at 4:33:06 AM UTC-8, Johan Haleby wrote:


On Mon, Nov 28, 2016 at 8:44 AM, Tim R <tim...@gmail.com> wrote:
Johan,

I understand the two testers use two different types of jsonpath - GPath and Rest-Assurred JsonPath.
I totally agree - its extremely confusing to use the same name for both ...

I think you've missunderstood. REST Assured includes a library called json-path (group id io.rest-assurued) which uses the GPath syntax. Then there's a Jayway library called jsonpath which uses the "json path" syntax and this is totally different from rest-assured. These two have just two things in common, the names are confusingly similar and they can extract values from a json document (with different syntax).
 

My point was if "$.*" works on both testers it should work with rest-assured regardless of which of the two JsonPath libs
are used. Am I correct with this assumption?

No
 

From your first answer above, it sounds like trying to grab the map of  <String, Customer> directly from the root - is either a bug or was never implemented in RA.
I will enter an issue as you suggested.

It's not a bug in RA, I would think it's a feature suggestion to the groovy-json module (JsonSlurper).
 

In the end, I'm just looking for a way to get the Customer data out of the response.
In Jersey I use this:
Map<String, Customer> map =  response.readEntity(new GenericType<Map<String, Customer>>() { });

Can you suggest a correct "path" string that will get me the data I'm looking for?

Hmm maybe you can do like this (with REST Assured json path):

JsonPath jsonPath = JsonPath.from(<json>)
Map<String, Customer> customers = jsonPath.getMap("$", String.class, Customer.class)

the above throws the cast exception: Cannot convert class java.util.HashMap to class com.mag.model.Customer

However, I came up with the following  two solutions that work. Both require a 2 step approach:
1. parse  to Map<String, HashMap>
2. parse the HashMap to Customer


        Map<String, HashMap> map = jsonPath.getMap("$", String.class, HashMap.class);   // don't parse/slurp the embedded map
        List<Customer> customers = new ArrayList<Customer>();

       // A - using jackson's objectmapper
        ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
        for (Map.Entry<String, HashMap> entry : map.entrySet())
        {
            customers.add(mapper.convertValue(entry.getValue(), Customer.class));
        }

        // B -  going from Map back to Json and then back to Object, no jackson
        Gson gson = new Gson();
        for (Map.Entry<String, HashMap> entry : map.entrySet())
        {
          jsonPath = JsonPath.from(gson.toJson(entry.getValue()));
          customers.add(jsonPath.getObject("$", Customer.class));
        }

If you have a better way to do this, please let me know.

thanks for the help


Johan Haleby

unread,
Dec 1, 2016, 1:33:18 AM12/1/16
to rest-a...@googlegroups.com
Sorry I'm not aware of any other solution off the top of my head. If you manage to improve it please let us know.

To unsubscribe from this group and stop receiving emails from it, send an email to rest-assured+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages