UnrecognizedPropertyException in FasterXML - jackson

I read String "{"name":"John","timestamp":"2020-08-14T11:47:52.297194Z"}" when i convert it into POJO using fasterXML i get the below exception,
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "timestamp" (class com.job.model.Person), not marked as ignorable (2 known properties: "name", "timeStamp"])
My POJO is,
#Data
#NoArgsConstructor
#Table(keyspace = "keyspace", name = "testTable")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "name")
private String name;
#Column(name = "timeStamp")
//#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC") // Tried with this no luck.
private Instant timeStamp;
}
I added the required dependency from the below url,
https://github.com/FasterXML/jackson-modules-java8, and also
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
ObjectMapper objectMapper = JsonMapper.builder()
.addModule(new ParameterNamesModule())
.addModule(new Jdk8Module())
.addModule(new JavaTimeModule())
.build();
is registered.

Json has timestamp while pojo has timeStamp. Either rename in pojo or use #JsonProperty("timestamp")
#JsonProperty("timestamp")
private Instant timeStamp;

Related

MockMvc POST returning 400 - expecting 201

I'm trying to write a controller unit test for a #PostMapping but am getting a failed test
Status expected:<201> but was:<400>
The controller works as expected in Postman so I know it actually works, but it would be nice to have a working unit test as well.
What am I doing wrong?
TEST
#Test
#DisplayName("CREATE NEW ENFORCEMENT ACTION")
void testCreateNewEnforcementAction() throws Exception {
EnforcementAction mockAction = new EnforcementAction();
mockAction.setSystemId(1289);
mockAction.setCurrentStaff("ralbritton");
mockAction.setCurrentStatus("NEEDED");
mockAction.setCreatedOn(LocalDateTime.now());
mockAction.setCreatedBy("ralbritton");
mockAction.setEaType("IF");
mockAction.setEaCode("CAP");
mockAction.setDeleted(false);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(mockAction);
mockMvc.perform(MockMvcRequestBuilders.post("/api/enforcementactions/action")
.contentType(MediaType.APPLICATION_JSON)
.content(json)
.characterEncoding("utf-8"))
.andExpect(status().isCreated()); //Have also tried this as .isOK() (didn't make a diff)
//.andReturn(); ///Added and removed this line to see if it made a differnce (it did not)
}
CONTROLLER BEING TESTED
#PostMapping("/api/enforcementactions/action")
public ResponseEntity<?> createNewEnforcementAction(#RequestBody EnforcementAction newAction) {
service.createEnforcementAction(newAction);
return new ResponseEntity<>(newAction, HttpStatus.CREATED);
}
MODEL
UPDATE: I'm adding in the model to show that there is not Bean Validation on fields
public class EnforcementAction {
private Integer eaId;
private Integer systemId;
private String alternateNumber;
private String systemName;
private Integer tenschdId;
private String currentStaff;
private String currentStatus;
private LocalDate dateActionIssued;
private LocalDate dateActionClosed;
private boolean deleted;
private LocalDateTime createdOn;
private String createdBy;
private LocalDateTime modifiedOn;
private String lastModifiedBy;
private String eaType;
private String eaCode;
private String comment;
private Long daysSinceCreate;
private List<EaStaffHistory> staffAssigned = new ArrayList<>();
private List<EaDocStatusHistory> documentStatus = new ArrayList<>();
private List<EaComments> eaComments = new ArrayList<>();
/** Constructors */
public EnforcementAction() {
}
public EnforcementAction(Integer eaId, Integer systemId, String systemName, Integer tenschdId,
String currentStaff, String currentStatus, Long daysSinceCreate,
String createdBy, String lastModifiedBy, LocalDate dateActionIssued, LocalDate dateActionClosed,
String eaType, String eaCode, LocalDateTime createdOn) {
this.eaId = eaId;
this.systemId = systemId;
this.tenschdId = tenschdId;
this.systemName = systemName;
this.currentStaff = currentStaff;
this.currentStatus = currentStatus;
this.createdBy = createdBy;
this.lastModifiedBy = lastModifiedBy;
this.dateActionClosed = dateActionClosed;
this.dateActionIssued = dateActionIssued;
this.eaType = eaType;
this.eaCode = eaCode;
this.daysSinceCreate = daysSinceCreate;
this.createdOn = createdOn;
}
...getters and setters....
POSTMAN showing successful post:
EDIT: I've updated the OP code to reflect current state. Still having the same issue though.
The reason for the 400 is how you send your payload to your controller. You are not serializing the Java object to JSON, but use the .toString() representation of it:
.content(String.valueOf(mockAction)))
Either make use of the ObjectMapper or prepare a custom JSON string:
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(mockAction);
mockMvc.perform(MockMvcRequestBuilders
.post("/api/enforcementactions/action")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isCreated());
OK so I finally figured out my problem and I'm posting it here in case someone else has the same issue. While #Rieckpil was correct in all his suggestions (and I will mark his answer as correct) the other problem I was having was in my mockAction object. I had:
mockAction.setCreatedOn(LocalDateTime.now())
Even though createdOn is of type LocalDateTime it was getting deconstructed in the body to look like this:
"createdOn": {
"dayOfWeek": "WEDNESDAY",
"dayOfYear": 204,
"month": "JULY",
"year": 2020,
"dayOfMonth": 22,
"hour": 12,
"minute": 43,
"monthValue": 7,
"nano": 839000000,
"second": 10,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
}
When I passed this as the createdOn variable into Postman I was able to get a meaningful error .HttpMessageNotReadableException: JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string. at [Source: (PushbackInputStream); line: 12, column: 21] (through reference chain: gov.deq.utah.enforcementactions.models.enforcementActions.EnforcementAction["createdOn"])
The test passed when I removed this. I kept all other suggestions provided.

Keep type when converting Object to Map

Jackson version: 2.9.8
I'm converting an object not to JSON, but to a Map, using objectMapper.convertValue(myObject, Map.class)
Here is a simple example to reproduce. My Pojo:
public static class Foo {
private final String string;
private final LocalDate date;
public Foo(String string, LocalDate date) {
this.string = string;
this.date = date;
}
public String getString() {
return string;
}
public LocalDate getDate() {
return date;
}
}
Conversion code:
public static void main(String[] args) {
Foo foo = new Foo("hello", LocalDate.of(1999, 12, 31));
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.convertValue(foo, Map.class);
Object string = map.get("string");
System.out.println("string: >>>"+ string +"<<< of type "+ string.getClass().getName());
Object date = map.get("date");
System.out.println("date: >>>"+ date +"<<< of type "+ date.getClass().getName());
}
This prints:
string: >>>hello<<< of type java.lang.String
date: >>>{year=1999, month=DECEMBER, chronology={id=ISO, calendarType=iso8601}, era=CE, dayOfMonth=31, dayOfWeek=FRIDAY, dayOfYear=365, leapYear=false, monthValue=12}<<< of type java.util.LinkedHashMap
When enabling the JavaTimeModule with
objectMapper.registerModule(new JavaTimeModule());
it prints:
string: >>>hello<<< of type java.lang.String
date: >>>[1999, 12, 31]<<< of type java.util.ArrayList
But what I need is to KEEP the LocalDate instance in the Map. No conversion. I can't find how to configure Jackson to keep (not convert) a type.
I have tried with a noop converter:
private static class KeepLocalDateJsonSerializer extends JsonSerializer<LocalDate> {
#Override
public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeObject(localDate);
}
};
registering it with
SimpleModule mod = new SimpleModule("KeepLocalDate");
mod.addSerializer(LocalDate.class, new KeepLocalDateJsonSerializer());
objectMapper.registerModule(mod);
and this leads to a StackOverflowError with detail:
ReadOnlyClassToSerializerMap.typedValueSerializer(ReadOnlyClassToSerializerMap.java:85)
JsonMappingException: Infinite recursion $Foo["date"]
Apparently Jackson tries to convert the returned value also. Looks like a bug to me. If the converter writes the same object through, even the same instance, then obviously the intention is to keep it. When converting to JSON then I understand we can't keep a LocalDate instance, but in a Map that's not the case.
How can I achieve this, in a generic way, so that it works for any data structure? Without annotations in the Pojo. So that all instances of a certain data type are passed through untouched.
There is this similar question Jackson Convert Object to Map preserving Date type but it's 5 years old, uses Date not LocalDate, and the solution with SerializationFeature.WRITE_DATES_AS_TIMESTAMPS does not work here.

Eclipselink not converting oracle.sql.TIMESTAMPTZ

I am getting an error when I am fetching value from DB using Eclipselink as persistence provider. It is not converting oracle.sql.TIMESTAMPTZ to java.sql.Timestamp or to java.util.Date.
Query q = em.createNativeQuery("SELECT * FROM MY_Schema.MyTable MT WHERE MT.START_DT = (SELECT MAX(START_DT) FROM MY_Schema.MyTable)",MyTable.class);
#Entity
#Table(name = "MyTable", schema = "MY_Schema")
public class MyTable implements Serializable {
#EmbeddedId
private MyTableId id;
#Embeddable
public class MyTableId implements Serializable {
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "END_DT")
private Calendar endTime;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "START_DT")
private Calendar startTime;
Exception
Caused by: Exception [EclipseLink-3001] (Eclipse Persistence Services- 2.6.8.WAS-v20181218-0accd7f): org.eclipse.persistence.exceptions.ConversionException ExceptionDescription: The object [oracle.sql.TIMESTAMPTZ#6156ebf7], of class[class oracle.sql.TIMESTAMPTZ], could not be converted to [class java.sql.Timestamp]. at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConverted(ConversionException.java:78) at org.eclipse.persistence.internal.helper.ConversionManager.convertObjectToTimest mp(ConversionManager.java:751) at org.eclipse.persistence.internal.helper.ConversionManager.convertObject(ConversionManager.java:112)
Things I found
While debugging I found that in eclipselink ConversionManager class
there is no handling for oracle.sql.TIMESTAMPTZ. It is directly throwing exception in this method.
/**
* INTERNAL:
* Build a valid instance of java.sql.Timestamp from the given source object.
* #param sourceObject Valid object of class java.sql.Timestamp, String, java.util.Date, or Long
*/
protected java.sql.Timestamp convertObjectToTimestamp(Object sourceObject) throws ConversionException {
java.sql.Timestamp timestamp = null;
if (sourceObject instanceof java.sql.Timestamp) {
return (java.sql.Timestamp)sourceObject;// Helper timestamp is not caught on class check.
}
if (sourceObject instanceof String) {
timestamp = Helper.timestampFromString((String)sourceObject);
} else if (sourceObject instanceof java.util.Date) {// This handles all date and subclasses, sql.Date, sql.Time conversions.
timestamp = Helper.timestampFromDate((java.util.Date)sourceObject);
} else if (sourceObject instanceof Calendar) {
return Helper.timestampFromCalendar((Calendar)sourceObject);
} else if (sourceObject instanceof Long) {
timestamp = Helper.timestampFromLong((Long)sourceObject);
} else {
throw ConversionException.couldNotBeConverted(sourceObject, ClassConstants.TIMESTAMP);
}
return timestamp;
}
oracle.sql.TIMESTAMPTZ handling is DB specific and done by subclasses of the Oracle9Platform. Make sure you have specified the correct target-database platform class that matches your database using the 'target-database' persistence property

Jackson + FAIL_ON_MISSING_CREATOR_PROPERTIES + Lombok

I'm attempting to use a Jackson flag on the objectMapper
objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);
This should cause object deserialization to fail if a constructor argument is not set in the json. i.e. If a field is missing as opposed to being set to null.
But I noticed that it only works if the object I want to deserialize has a constructor like so
public MyObject(#JsonProperty("id") UUID id, #JsonProperty("url") URL url) {
this.id = id;
this.url = url;
}
That's a little problematic as I'd hoped to use lombok's #AllArgsConstructor to generate the constructor. But if the constructor is missing the #JsonProperty(..) the FAIL_ON_MISSING_CREATOR_PROPERTIES check does not work. Instead the parameters are passed in as null.
I've come across some solutions here Can't make Jackson and Lombok work together. But so far they're not working for me.
Any suggestions?
--- Update ---
The annotations on my class are
#Data
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
#JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class MyClass { ... }
The following combination of annotations work fine with Lombok 1.18.0 and Jackson 2.9 (the most recent versions as of July 2018):
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public static class Foo {
private UUID id;
private String url;
}
String json = "{\n" +
" \"id\": \"32783be3-5355-41d2-807b-619e3481d220\",\n" +
" \"url\": \"http://example.com\"\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);

Swagger responseContainer = "List" not working

Below is my interface
#Api(value = "/abc/def", basePath = "/abc/def", description = "")
public interface test{
#ApiOperation(value = "some description" , response = SomeDTO.class ,responseContainer = "List" )
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successful response"),
})
#GET
#Path("/{Id}")
List<SomeDTO> getById(#PathParam("Id") String Id);
}
In the Swagger UI when i provide the Id and Click on Try out it doesn't display the list of results . It just hangs.
Below are my Dependencies
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.3.10</version>
</dependency>
SomeDTO Looks like below in Swagger UI
SomeDTO {
Id (integer, optional),
TestType (TestType, optional) = ['QA' or 'PROD'], // enum in java class
Time (string, optional),
Status (Status, optional) = ['Y' or 'N'], // enum in java class
}
SomeDTO.java
public class SomeDTO {
private Integer Id;
private TestType testType;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss")
private Date Time;
private Status status;
// getters and setters
}
I am able to get the results by Chrome Poster Plugin .
Can someone please let me know what i have missed while configuring swagger to return a list of SomeDTO types..