Jackson parsing exception -(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value

SUMIT picture SUMIT · Jun 13, 2018 · Viewed 21.7k times · Source
  • Spring Boot Version : 1.5.10
  • Jackson Version : 2.9.5
  • Lombok Version : 1.18.0

I have a scenario where I am sending payload using kafka. On receiving that payload, I am trying to assert whether both payload at receiver and sender end are same or not.

First I have created a class which will be passed as payload. Structure of the class is gievn below. Used lombok plugin version is 1.18.0.

@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class MyDummyClass implements Serializable{

    private static final long serialVersionUID = -4181985100899233094L;
    private String data;
    private String id;
}

For the above pojo I have created an unit test where I am passing a string and trying to convert that from String to object which is working without any issue.

@Test
public void shouldBeAbleToConvertStringToDesiredObjectType() throws IOException {
    String s = "{\r\n  \"data\" : \"foo\",\r\n  \"id\" : \"xyz\"\r\n}";
    MyDummyClass myDummyClass = convertValue(s, MyDummyClass.class);
    assertThat(myDummyClass.getData(), is("foo"));
}

Also jackson mapper configuration is given below below.

private static final ObjectMapper mapper = new ObjectMapper();

static {
    mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES);
    mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    mapper.enable(ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
    // Skip the Null Values
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.disableDefaultTyping();

    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); //YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45.003+01:00)
    dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    mapper.setDateFormat(dateFormat);
}

Now coming to main problem statement. So in my another test case where I am sending payload through kafka and after receiving the response from the kafka topic, I am trying to convert incoming String data to the desired MyDummyClass class type. In my test case I have put logger statement to see what value I am receiving. I can see I am getting the exactly same string value mentioned in the above test case. But during parsing of that text to desired MyDummyClass type I am getting error (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value.

@Test
public void messageWithAnyContractObjectCanBeConvertedToSameObjectAtTheListenerEnd() throws InterruptedException, IOException, JSONException {
    String correlationID = UUID.randomUUID().toString();
    String id = UUID.randomUUID().toString();
    MyDummyClass actualPayload = MyDummyClass.builder().data("foo").id("xyz").build();
    Message message = MessageBuilder.withAnyMessage()
            .withNoHeader(BaseHeader.builder().ID(id).correlationID(correlationID).sendToDestination("my-topic").build())
            .payload(actualPayload)
            .build();
    messagePublisher.publishMessage(message, DEFAULT_PUBLISHER_OPTIONS);

    String recordedString = records.poll(10, TimeUnit.SECONDS).value();
    LOGGER.info("Receiving Response {}", recordedString);
    MyDummyClass recordedValue = convertValue(recordedString, MyDummyClass.class);

    assertThat(recordedValue.getData(), is(actualPayload.getData()));
}

Detailed Error Log: enter image description here

Answer

SUMIT picture SUMIT · Jun 21, 2018

Finally I was able to solve this problem. This issue was a result of wrong code written in side my publisher code where, first I was encrypting my payload by converting my entried payload as json object (using jackson), later I was again storing that json payload inside object with header and trying to convert that object into string(again using jackson converter). In this process, I was converting entire payload to string twice, as a result I was introducing extra \r\n into my payload which I was sending through kafka. While converting back to object from json to object, this extra \r\n was causing the problem what I pasted earlier.

As a solution, I have stored the already converted json object into another object with jackson annotation @JsonRawData . This prevent adding extra \r\n.