Tuesday, July 17, 2018

Spring Boot JacksonTester custom serializer not registered

Leave a Comment

Trying to run the through Jackson library for some upcoming work. I've got the following test model:

Address.java

package com.example.domain;  import java.time.LocalDate;  import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle;  import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;  public class Address {      private String addressKey;     private String postalCode;     private String country;      @JsonSerialize(using = DateSerializer.class, as = String.class)     @JsonDeserialize(using = DateDeserializer.class, as = LocalDate.class)     private LocalDate dateEntered;      public String getAddressKey() {         return addressKey;     }      public void setAddressKey(String addressKey) {         this.addressKey = addressKey;     }      public String getPostalCode() {         return postalCode;     }      public void setPostalCode(String postalCode) {         this.postalCode = postalCode;     }      public String getCountry() {         return country;     }      public void setCountry(String country) {         this.country = country;     }      public LocalDate getDateEntered() {         return dateEntered;     }      public void setDateEntered(LocalDate dateEntered) {         this.dateEntered = dateEntered;     }      @Override     public int hashCode() {         return HashCodeBuilder.reflectionHashCode(this, false);     }      @Override     public boolean equals(Object obj) {         return EqualsBuilder.reflectionEquals(this, obj, false);     }      @Override     public String toString() {         return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE, false);     } } 

and a custom serializer for the dateEntered field:

DateSerializer.java

package com.example.domain;  import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter;  import org.springframework.boot.jackson.JsonComponent;  import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider;  @JsonComponent public class DateSerializer extends JsonSerializer<LocalDate> {     @Override     public void serialize(LocalDate lc, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)             throws IOException, JsonProcessingException {         String date = lc.format(DateTimeFormatter.ofPattern("dd MM yyyy"));         jsonGenerator.writeString(date);     } } 

I've put together the following test, leveraging the @JsonTest annotation to check if the serializer class is doing what i think it should:

AddressTest.java

@JsonTest @RunWith(SpringRunner.class) public class AddressTest {  @Autowired private JacksonTester<Address> json;      @Test     public void test_Address_serialisation_dateEnteredSerialised() throws Exception {         final Address address = getTestAddress();          assertThat(json.write(address))             .hasJsonPathStringValue("@.dateEntered")             .extractingJsonPathStringValue("@.dateEntered")             .isEqualTo("01 05 2018");     }      private Address getTestAddress() {         final Address testAddress = new Address();         testAddress.setAddressKey("testAddressKey");         testAddress.setCountry("testCountry");         testAddress.setPostalCode("testPostalCode");         testAddress.setDateEntered(LocalDate.of(2018, 5, 1));         return testAddress;     }  } 

When i run the serialize test, I get this in console:

com.fasterxml.jackson.databind.JsonMappingException: Invalid definition for property dateEntered (of type 'Lcom/example/domain/Address;'): Can not refine serialization type [simple type, class java.time.LocalDate] into java.lang.String; types not related  at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284) at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110) at com.fasterxml.jackson.databind.SerializerProvider.reportBadPropertyDefinition(SerializerProvider.java:1180) at com.fasterxml.jackson.databind.ser.PropertyBuilder.buildWriter(PropertyBuilder.java:101) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._constructWriter(BeanSerializerFactory.java:816) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:610) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:399) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:282) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:234) at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:168) at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1308) at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1279) at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:536) at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:743) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:330) at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1427) at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1158) at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:1031) at org.springframework.boot.test.json.JacksonTester.writeObject(JacksonTester.java:116) at org.springframework.boot.test.json.AbstractJsonMarshalTester.write(AbstractJsonMarshalTester.java:133) at com.example.domain.AddressTest.test_Address_serialisation_dateEnteredSerialised(AddressTest.java:68) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) 

From what I could gather, adding @JsonComponent to the classwould allow it to be picked up and registered when used in conjunction with @JsonTest but based on this it doesnt seem like its being scanned and added. Am i missing a step in the test or src code? Its worth noting that the deserialiser is set up the same way(it toStrings the date in a particular format) and it works fine

1 Answers

Answers 1

Specifying as = ... within @JsonSerialize is rarely needed, especially when you also specify using = ....

Quoted from the javadoc of @JsonSerialize.as:

...
Note: if using is also used it has precedence (since it directly specifies serializer, whereas this would only be used to locate the serializer) and value of this annotation property is ignored.

In your case: Specifying as = String.class seems to be the culprit of the not found JSON serializer.

If you omit it and in your Address class just write

@JsonSerialize(using = DateSerializer.class) @JsonDeserialize(using = DateDeserializer.class) private LocalDate dateEntered; 

then the DateSerializer is found and the AddressTest runs fine without exception.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment