7

I am getting the below error message, can someone please help or suggest how best to debug this.

Cannot deserialize instance of java.lang.String out of START_OBJECT token at [Source: (PushbackInputStream); line: 1, column: 37610] (through reference chain: com.model.ProductList["products"]->java.util.ArrayList[23]->com.model.Product["price"]->com.Price["now"])

I am trying to deserialise a Products object from a REst API call. The code has been working fine until I added code to deserialise a Price sub-class. This looks as follows,

"price": {
        "was": "",
        "then1": "",
        "then2": "",
        "now": "59.00",
        "uom": "",
        "currency": "GBP"
      },

My Price pojo looks as follows,

public class Price {

     @JsonProperty("was")
     String was;
     @JsonProperty("then1")
     String then1;
     @JsonProperty("then2")
     String then2;
     @JsonProperty("now")
     String now;
     @JsonProperty("uom")
     String uom;
     @JsonProperty("currency")
     String currency;

     public Price() {
         //blank constructor for JSON
     }

     @Override
     public String toString() {
         return "Price{" +
                 "was='" + was + '\'' +
                 ", then1='" + then1 + '\'' +
                 ", then2='" + then2 + '\'' +
                 ", now='" + now + '\'' +
                 ", uom='" + uom + '\'' +
                 ", currency='" + currency + '\'' +
                 '}';
     }
}

I have written a Junit test to try and simulate the error but it works in my test,

    @Test
    public void shouldConvertJsonProductListIntoPrice() {
        ObjectMapper objectMapper = new ObjectMapper();
        String content3 = "{\"products\": [{\"productId\": \"3525085\",\"title\": \"hush Tasha Vest Dress\", " +
                "\"price\": {\"was\": \"\",\"then1\": \"\",\"then2\": \"\",\"now\": \"59.00\",\"uom\": \"\",\"currency\": \"GBP\"}, " +
                "\"colorSwatches\": [{\"basicColor\": \"Red\",\"skuId\": \"237494589\"},{\"basicColor\": \"Blue\",\"skuId\": \"237494562\"}] " +
                "}]}";
        JavaType valueType = objectMapper.constructType(ProductList.class);
        ProductList readValue;
        try {
            readValue = objectMapper.readValue(content3, valueType);
            assertEquals("3525085", readValue.getProductList().get(0).productId);
            assertEquals("hush Tasha Vest Dress", readValue.getProductList().get(0).title);
            assertEquals("", readValue.getProductList().get(0).price.then1);
            assertEquals("59.00", readValue.getProductList().get(0).price.now);
            assertEquals("Blue", readValue.getProductList().get(0).colorSwatches[1].basicColor);
            assertEquals("237494562", readValue.getProductList().get(0).colorSwatches[1].skuId);
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }

If I comment out the "now" field then my RestAPI call works perfectly and I dont see the exception. So it seems that there is a problem with the "now" field and here I notice that its trying to convert "59.00" into a String. Could this be a problem for the Fasterxml converter ? do I need to help it maybe ?

The Product class is as follows (although this is much reduced list of fields that I am receiving off the API call).

public class Product {

     @JsonProperty("productId")
     String productId;

     @JsonProperty("title")
     String title;

     @JsonProperty("colorSwatches")
     ColorSwatch [] colorSwatches;

     @JsonProperty("price")
     Price price;

     public Product(){
         // blank required for Jackson
     }

     public Product(String productId, String title, ColorSwatch[] colorSwatches, Price price){
         this.productId = productId;
         this.title = title;
         this.colorSwatches = colorSwatches;
         this.price = price;
  }
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
robbie70
  • 1,515
  • 4
  • 21
  • 30
  • Why are you parsing and constructing JSON Manually? Why not let Spring/Jackson do this for you? – Todd Jan 06 '19 at 14:29
  • Hi Todd, the error I reported above is coming from Spring/Jackson. I wrote the Unit Test to try and see if I could reproduce the error but I wasnt able to. Unless I have misunderstood you. Have I added code which I should be leaving to Spring/Jackson to do for me ? – robbie70 Jan 06 '19 at 14:37
  • I tried using your code and it works well! Show us your product class as well if you can. – ScanQR Jan 06 '19 at 14:37
  • thanks ScanQR - how did you test it ? I seem to get it when call the full RestAPI and the Spring/Jackson processes are handling the unmarshalling. – robbie70 Jan 06 '19 at 14:42
  • Your test case doesn't represent what you are doing. Your test case should test Products object which contains with Price object. Then you will get a real test case. – Jonathan JOhx Jan 06 '19 at 14:50
  • @JonathanJohx I've updated the question now by replacing the UnitTest of Price with one for Product and this works fine. – robbie70 Jan 06 '19 at 16:02
  • @ScanQR I've included the details for Product – robbie70 Jan 06 '19 at 16:02
  • 1
    @robbie70 I think you need to test it from ProductList class which you have. Please try to replicate the same values that you are doing. And can you add code where you can you are calling to the deserializer?thank you – Jonathan JOhx Jan 06 '19 at 16:49
  • @JonathanJohx I've updated the test for ProductList as you suggested, that was a good call. And the test still passes ! I am not quite sure how I can add debug code into the deserialiser part - that bit of the application seems to be hidden from me - the specific line I am executing in the Controller is shown below ResponseEntity restRes = restTemplate.exchange("https://jl-nonprod-syst.apigee.net/v1/categories/600001506/products?key=2ALHCAAs6ikGRBoy6eTHA58RaG097Fma", HttpMethod.GET, entity, ProductList.class); – robbie70 Jan 06 '19 at 23:20
  • @JonathanJohx As I mentioned in the question this works fine when I comment out the "now" field on my Price object. – robbie70 Jan 06 '19 at 23:23
  • Generally this kind of exception means that `JSON` does not fit `POJO` classes. Lets try a trick, instead of class `Price` in `Product` use `Map`. For example: `Map price` and try to deserialise. In case it will success add a `String` representation of that `Map` to a question. – Michał Ziober Jan 06 '19 at 23:42

1 Answers1

15

The error states it expects a VALUE (VALUE_STRING preferably), whereas it gets a START_OBJECT, so your issue is probably coming from a json of the form

"price": {
    "was": "",
    "then1": "",
    "then2": "",
    "now":  {
        ...
    }
    "uom": "",
    "currency": "GBP"
},

Instead of the "now": "some value" form expected by the code.

Loïc Le Doyen
  • 975
  • 7
  • 16
  • wow well done I think that might be it. I just searched the record set and found at record 25 of 50 an entry similar to how you've described, – robbie70 Jan 08 '19 at 22:22
  • { "productId": "3341058", "type": "product", "title": "Jolie Moi Bonded Lace Prom Dress", "code": "69251713", "averageRating": 0, "reviews": 0, "price": { "was": "85.00", "then1": "68.00", "then2": "", "now": { "from": "59.00", "to": "68.00" }, "uom": "", "currency": "GBP" }, – robbie70 Jan 08 '19 at 22:24
  • its a pity I cant format the record as nicely as you have in your example. Tomorrow I will check it in my app but I am pretty sure thats it. – robbie70 Jan 08 '19 at 22:24
  • If that is the case then do you know how I should reprensent the data type - it looks like it can either be sent as a String or an Object Type - how should I define that in my pojo ? – robbie70 Jan 08 '19 at 22:25
  • thank you your tip has helped me to fix my problem. I changed the "now" field in my Price pojo from String to Object and re-ran my project and it now works. I will need to develop some way now of deserialising this Object for either a String or a field which can contain two sub-fields "from" and "to" as shown above. I have marked this question as answered. – robbie70 Jan 09 '19 at 18:28
  • 1
    You are welcome. For deserializing a node that can be either a String or an Object, you could give a look to `@JsonSerialize` giving a custom `JsonDeserializer` – Loïc Le Doyen Jan 09 '19 at 19:32