Jackson not populating all properties - java

I am working on a simple example using Jackson library to convert a json string back to Java object but I see only few properties are being set on my java object instead of all properties.
Here is my code:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.codehaus.jackson.map.ObjectMapper;
public class JsonTest {
public static void main(String[] args) throws FileNotFoundException, IOException {
StringBuffer buffer = new StringBuffer();
String data = "";
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("path-to-sample.json"));
while ((data = reader.readLine()) != null) {
buffer.append(data);
}
} finally {
if (reader != null) {
reader.close();
}
}
System.out.println(buffer.toString());
ObjectMapper mapper = new ObjectMapper();
Sample obj = mapper.readValue(buffer.toString(), Sample.class);
System.out.println(obj);
}
}
The Sample.java program looks like this:
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Sample {
#JsonProperty("prop_1")
private String prop1;
private String prop2;
#JsonProperty("prop_3")
private String prop3;
private String prop4;
// Setters & Getters for the properties.
#Override
public String toString() {
return "Sample [prop1=" + prop1 + ", prop2=" + prop2 + ", prop3="
+ prop3 + ", prop4=" + prop4 + "]";
}
}
Input json string in my file is :
{
"prop_1": "1",
"prop2": "2",
"prop_3": "3",
"prop4": "4"
}
The output of this program is :
Sample [prop1=null, prop2=2, prop3=null, prop4=4]
As per my program the prop1 and prop3 should not be null. I am not clear where I made mistake.
Update:
If I remove the #JsonProperty annotation then I am getting the exception as :
Exception in thread "main" org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "prop_1" (Class Sample), not marked as ignorable
This is my pom.xml file dependencies:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>

You said in your comment, that you're using Jackson in version "2.5.4", but you're importing the ObjectMapper class from the org.codehaus package. This means, that this class is from version 1.9.13 (or from an older version).
I can reproduce your problem if I mix the versions using ObjectMapper and JsonIgnoreProperties from version 1.9.13 (org.codehaus) and JsonProperty from version 2.6.0 (com.fasterxml).
Output:
Sample [prop1=null, prop2=2, prop3=null, prop4=4]
If I only use version 1.9.13 or 2.6.0, then the result is ok:
Sample [prop1=1, prop2=2, prop3=3, prop4=4]
(for both)
So I recommend to make sure that you don't mix the used libraries and I recommend to use the newest version, which is from FasterXML.
But the used version is up to you.
You can download the jar file from here:
org.codehaus.jackson v1.9.13
com.fasterxml.jackson (core) v2.6.0
Btw about your comment:
#OldCurmudgeon, Thanks for responding. Changing the fields to public has not fixed the issue. I have removed the #JsonProperty annotation and then changed the setter methods to setProp_1 & setProp_3, it worked. So does it mean that there is an issue with #JsonProperty annotation?
Yes, you have (or hopefully had :P) a problem with that annotation: it was from a different Jackson version.
About your edit:
The link to the Jackson lib from fasterXML in the maven repository has one big advantage: it shows you which lib you should download to work with Jackson in your project.
You need:
Jackson Databind (which also has the ObjectMapper class)
Jackson Core
Jackson Annotations

Related

How to serialise a POJO to be sent over a Java RSocket?

I'm trying to send a POJO over an RSocket requestStream:
import java.io.Serializable;
class GreetingRequest implements Serializable {
private String name;
public GreetingRequest() {}
public GreetingRequest(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If I were to send a String I can do:
ByteBuf data = ByteBufAllocator.DEFAULT.buffer().writeBytes("Hello".getBytes());
socket.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
But how can I serialise my POJO?
This is my attempt using implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' which doesn't work:
GreetingRequest pojo = new GreetingRequest("Davide");
ByteBuf data = SerializationUtils.serialize(pojo);
socket.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
There is a native java serialization mechanism that I would NOT recommend, but you can read about it. Read about Serialazable interface in Java API. There are 2 options that I would recommend:
JSON-JACKSON (also known as Faster XML)
GSON (mentioned in the answer from César Ferreira)
Both convert classes to JSON and vise-versa. For JSON-JACKSON see class ObectMapper. In particular methods writeValueAsString() or writeValueAsBytes() to serialize your object to JSON string or bytes. And to convert it back look for method readValue().
Here are the Maven artifacts that you would need to use it:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.3</version>
</dependency>
I recommend you using Gson converter. It helps you to convert a Java Class to a JSON String. And then you can work with the String as if you were working with simple text.
You can import the dependency:
dependencies {
implementation 'com.google.code.gson:gson:2.8.7'
}
And then, can use jsonschema2pojo to convert the JSON:
{ "name": "Test" }
to classes like this:
package com.example;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class GreetingRequest {
#SerializedName("name")
#Expose
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
After all is done you can do something like this in Java:
Gson converter = new Gson();
GreetingsRequest request = new GreetingRequest();
request.setName("Test");
String greetingsJSON = converter.toJson(request);
And then you can still send the JSON string as it follows:
ByteBuf data = ByteBufAllocator.DEFAULT.buffer().writeBytes(greetingsJSON.getBytes());
socket.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);
Data conversions:
JSON Object - Java class
Array - List<>
Helpful links:
This is the library you need to include (tutorials included) in Java: GSON Converter Git
This is an JSON to Class online converter: Jsonschema2pojo generator
If you use a framework like Spring Boot this is taken care of for you. You may want to manually control in which case the other examples are more relevant, but there are productivity benefits to Spring Boot or rsocket-rpc.
https://github.com/rsocket/rsocket-demo/blob/master/src/main/kotlin/io/rsocket/demo/chat/ChatController.kt
https://spring.io/blog/2020/03/02/getting-started-with-rsocket-spring-boot-server
or rsocket-rpc-java using protobuf instead of Serialization
https://github.com/rsocket/rsocket-rpc-java/blob/master/docs/get-started.md

Parsing Yaml in Java

I have the following YAML I want to parse using Jackson parser in Java.
android:
"7.0":
- nexus
- S8
"6.0":
- s7
- g5
ios:
"10.0":
- iphone 7
- iphone 8
I created a created class which has getter and setter as Java Object for android. It works fine. But how do I do the same for 6.0 and 7.0? I'm usingJackson` Parser
No idea whether Jackson supports that; here's a solution with plain SnakeYaml (I will never understand why people use Jackson for parsing YAML when all it does is basically take away the detailed configuration possible with SnakeYaml which it uses as backend):
class AndroidValues {
// showing what needs to be done for "7.0". "8.0" works similarly.
private List<String> v7_0;
public List<String> getValuesFor7_0() {
return v7_0;
}
public void setValuesFor7_0(List<String> value) {
v7_0 = value;
}
}
// ... in your loading code:
Constructor constructor = new Constructor(YourRoot.class);
TypeDescription androidDesc = new TypeDescription(AndroidValues.class);
androidDesc.substituteProperty("7.0", List.class, "getValuesFor7_0", "setValuesFor7_0");
androidDesc.putListPropertyType("7.0", String.class);
constructor.addTypeDescription(androidDesc);
Yaml yaml = new Yaml(constructor);
// and then load the root type with it
Note: Code has not been tested.
I think that you should try annotation com.fasterxml.jackson.annotation.JsonProperty. I'll provide a short example below.
Sample YAML file:
---
"42": "some value"
Data transfer object class:
public class Entity {
#JsonProperty("42")
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Parser:
public class Parser {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Entity entity = mapper.readValue(new File("src/main/resources/sample.yml"), Entity.class);
System.out.println(entity.getValue());
}
}
The console output should be: some value.
P.S. I tested it with the following dependencies:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>

Jackson (de)serialization of Java8 date/time by a JAX-RS client

I'm making a serivce client for a REST endpoint, using a JAX-RS client for the HTTP requests and Jackson to (de)serialize JSON entities. In order to handle JSR-310 (Java8) date/time objects I added the com.fasterxml.jackson.datatype:jackson-datatype-jsr310 module as a dependency to the service client, but I didn't get it to work.
How to configure JAX-RS and/or Jackson to use the jsr310 module?
I use the following dependencies:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>${jax-rs.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
I don't want to make the service client (which is released as a library) dependent on any specific implementation – like Jersey, so I only depend on the JAX-RS API. To run my integration tests I added:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
Instantiation of the JAX-RS client is done in a factory object, as follows:
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
#Produces
public Client produceClient() {
return ClientBuilder.newClient();
}
A typical DTO looks like this:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.time.Instant;
import static java.util.Objects.requireNonNull;
#JsonPropertyOrder({"n", "t"})
public final class Message {
private final String name;
private final Instant timestamp;
#JsonCreator
public Message(#JsonProperty("n") final String name,
#JsonProperty("t") final Instant timestamp) {
this.name = requireNonNull(name);
this.timestamp = requireNonNull(timestamp);
}
#JsonProperty("n")
public String getName() {
return this.name;
}
#JsonProperty("t")
public Instant getTimestamp() {
return this.timestamp;
}
// equals(Object), hashCode() and toString()
}
Requests are done like this:
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
public final class Gateway {
private final WebTarget endpoint;
public Message postSomething(final Something something) {
return this.endpoint
.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.json(something), Message.class);
}
// where Message is the class defined above and Something is a similar DTO
}
JSON serialization and deserialization works fine for Strings, ints, BigIntegers, Lists, etc. However, when I do something like System.out.println(gateway.postSomthing(new Something("x", "y")); in my tests I get the following exception:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.Instant` (no Creators, like default construct, exist): no String-argument constructor/factory method to deserialize from String value ('Fri, 22 Sep 2017 10:26:52 GMT')
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 562] (through reference chain: Message["t"])
at org.example.com.ServiceClientTest.test(ServiceClientTest.java:52)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `java.time.Instant` (no Creators, like default construct, exist): no String-argument constructor/factory method to deserialize from String value ('Fri, 22 Sep 2017 10:26:52 GMT')
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 562] (through reference chain: Message["t"])
at org.example.com.ServiceClientTest.test(ServiceClientTest.java:52)
From which I conclude that Jackson doesn't know how to deserialize Strings into Instants. I found blogs and SO questions about this topic, but I found no clear explanation on how to make it work.
Note that I'd like the service client to handle date strings like "Fri, 22 Sep 2017 10:26:52 GMT" as well as "2017-09-22T10:26:52.123Z", but I want it to always serialize to ISO 8601 date strings.
Who can explain how to make deserialization into an Instant work?
In the example code you're currently depending on jersey-media-json-jackson. You're probably better of by depending on Jackson's JAX-RS JSON as you are able to configure the Jackson mapper using the standard JAX-RS API (and of cource the Jackson API).
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
After removing the jersey-media-json-jackson and adding the jackson-jaxrs-json-provider dependency you can configure the JacksonJaxbJsonProvider and register it in the class that produces the Client:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import static com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS;
public class ClientProducer {
private JacksonJsonProvider jsonProvider;
public ClientProducer() {
// Create an ObjectMapper to be used for (de)serializing to/from JSON.
ObjectMapper objectMapper = new ObjectMapper();
// Register the JavaTimeModule for JSR-310 DateTime (de)serialization
objectMapper.registerModule(new JavaTimeModule());
// Configure the object mapper te serialize to timestamp strings.
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// Create a Jackson Provider
this.jsonProvider = new JacksonJaxbJsonProvider(objectMapper, DEFAULT_ANNOTATIONS);
}
#Produces
public Client produceClient() {
return ClientBuilder.newClient()
// Register the jsonProvider
.register(this.jsonProvider);
}
}
Hope this helps.
You can configure the Jackson ObjectMapper in a ContextResolver. The Jackson JAX-RS provider will lookup this resolver and get the ObjectMapper from it.
#Provider
public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperResolver() {
mapper = new ObjectMapper();
// configure mapper
}
#Override
public ObjectMapper getContext(Class<?> cls) {
return mapper;
}
}
Then just register the resolver like you would any other provider or resource in your JAX-RS application.

Jackson xml module: deserializing immutable type with a #JacksonXmlText property

I want to serialize an immutable type both as json and as xml:
The serialized JSON is like:
{
"text" : "... the text..."
}
and the serialized xml is like:
<asText>_text_</asText>
(note that the text is the xml's element text)
The java object is like:
#JsonRootName("asText")
#Accessors(prefix="_")
public static class AsText {
#JsonProperty("text") #JacksonXmlText
#Getter private final String _text;
public AsText(#JsonProperty("text") final String text) {
_text = text;
}
}
beware that the _text property is final (so the object is immutable) and it's annotated with #JacksonXmlText in order to be serialized as the xml element's text
Being the object immutable, a constructor from the text is needed and the constructor's argument must be annotated with #JsonProperty
public AsText(#JsonProperty("text") final String text) {
_text = text;
}
When serializing / deserializing to/from JSON everything works fine
... the problem arises when serializing / deserializing to/from XML:
// create the object
AsText obj = new AsText("_text_");
// init the mapper
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
log.warn("Serialized Xml\n{}",xml);
// Read from xml
log.warn("Read from Xml:");
AsText objReadedFromXml = mapper.readValue(xml,
AsText.class);
log.warn("Obj readed from serialized xml: {}",
objReadedFromXml.getClass().getName());
The exception is:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "" (class r01f.types.url.UrlQueryStringParam), not marked as ignorable (2 known properties: "value", "name"])
It seems that the xml module needs the object's constructor to be annotated like:
public AsText(#JsonProperty("") final String text) {
_text = text;
}
BUT this does NOT even works:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `test.types.SerializeAsXmlElementTextTest$AsText` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
The annotation #JsonProperty("text") at the constructor's argument is needed to deserialize from JSON
... how can i make this to work
Try adding a public getter for the property. I believe that should fix the deserialization issue.
#JsonRootName("asText")
#Accessors(prefix = "_")
public static class AsText {
#JsonProperty("text")
#JacksonXmlText
#Getter
private final String _text;
public AsText(#JsonProperty("text") final String text) {
_text = text;
}
public String getText() {
return _text;
}
}
Actually, it works without adding a getter too, with these versions of Lombak & Jackson.
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
</dependencies>
I had same issue with error "no delegate- or property-based Creator". In my case it was problem with Immutables version 2.5.6. I have fixed it by downgrade to version 2.5.5. Version 2.5.6 is available in mvnrepository.com but on official page is as stable version marked 2.5.5.
Update 2018
This hack worked in 2.9.0 but it seems to stop working after this commit. It is not clear if there is an easy way to make it work again.
It looks like the main reason for your issue is that you try to use JSON and XML serialization at the same time but with different configurations. Unfortunately XmlMapper inherits from ObjectMapper and inherits all the JSON-specific configuration (and you can override it but can not clear it with XML-specific annotations) which is the reason for your conflict. It seems that the simplest way to work this around is to use #JsonAlias annotation in the constructor. It is a bit hacky but it works. Here is a minimal example (without Lombok) that works for me:
#JsonRootName("asText")
public static class AsText {
#JsonProperty("text")
#JacksonXmlText
private final String _text;
public AsText(#JsonAlias("") #JsonProperty("text") final String text) {
_text = text;
}
#JsonIgnore
public String getText() {
return _text;
}
#Override
public String toString() {
return "AsText{" +
"_text='" + _text + '\'' +
'}';
}
}
Note that I also added #JsonIgnore to the getter because else I didn't get XML format you requested (and you can do the same using Lombok's onMethod as described at onX).
For a simple test:
public static void main(String[] args) throws IOException {
// create the object
AsText obj = new AsText("123_text_");
// init the mapper
//ObjectMapper mapper = new ObjectMapper();
XmlMapper mapper = new XmlMapper();
// write as xml
String xml = mapper.writeValueAsString(obj);
System.out.println("Serialized Xml\n" + xml);
// Read from xml
AsText objReadedFromXml = mapper.readValue(xml, AsText.class);
System.out.println("Read from Xml: " + objReadedFromXml);
}
I get the following output:
Serialized Xml
<asText>123_text_</asText>
Read from Xml: AsText{_text='123_text_'}

AWS Lambda json deserialization with jackson annotations

I'm calling an aws lambda with a json body. So the fields of the json are with different name from the ones in the POJO. So what I did is to add #JsonProperty on the fields to tell jackson what are the names in json. But for some reason it seems that it doesn't recognize them and all the fields are null. If I pass a json with the same field names as the POJO it's working. Here's my class:
public class Event implements Identifiable {
#JsonProperty("distinct_id")
private String distinctId;
#JsonProperty("user_id")
private Integer userId;
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime eventDateTime;
//Here are the getters and setters
}
If I pass
{"distinct_id":"123", "user_id":123, "dt":"2017-01-04T08:45:04+00:00"}
all the fields are null and with distinctId, userId, eventDateTime it's serializing ok with the exception that it also doesn't recognize my custom serializers/deserializers but this actually is the same problem.
My conclusion is that for some reason the aws jackson is not working with the annotations but it doesn't make sense.
So I found a way to do this. You need to implement RequestStreamHandler which gives you input and output streams which you can work with:
import com.amazonaws.services.lambda.runtime.RequestStreamHandler
public class ChartHandler implements RequestStreamHandler {
private ObjectMapper objectMapper = new ObjectMapper();
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
DeserializationClass deserializedInput = objectMapper.readValue(inputStream, DeserializationClass.class)
objectMapper.writeValue(outputStream, deserializedInput); //write to the outputStream what you want to return
}
}
Having the input and output streams makes you independent of the format and frameworks you use to parse it.
Take a look at this quote from AWS documentation:
You shouldn't rely on any other features of serialization frameworks such as annotations. If you need to customize the serialization behavior, you can use the raw byte stream to use your own serialization.
From: https://docs.aws.amazon.com/lambda/latest/dg/java-programming-model-req-resp.html
It sounds like you have version mismatch between annotation types, and databind (ObjectMapper): both MUST be the same major version. Specifically, Jackson 1.x annotations work with Jackson 1.x databind; and 2.x with 2.x.
Difference is visible via Java package: Jackson 1.x uses org.codehaus.jackson, whereas Jackson 2.x uses com.fasterxml.jackson. Make sure to import right annotations for ObjectMapper you use.
I had this same issue and needed MyCustomClass to be taken in and out of the Lambda Function correctly so that it can be passed through my State Machine in the Step Function without any hiccups.
Building off what Hristo Angelov posted, I was able to get a solution that worked for me and I'm posting it hoping that it will help others that were stuck like I was:
import java.io.InputStream;
import java.io.OutputStream;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
public class StaticSearchPagingLambdaFunctionHandler implements RequestStreamHandler {
LambdaLogger logger = null;
MyCustomClass myCustomClass = null;
// Register the JavaTimeModule for LocalDate conversion
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {
myCustomClass = objectMapper.readValue(inputStream, MyCustomClass .class);
// ...
// Do stuff with myCustomClass
// ...
objectMapper.writeValue(outputStream, myCustomClass);
}
}
Even though the JSON string will print out differently with the ObjectMapper writing to the OutPutStream, when the next lambda function takes it in while going through the Step Function, it will still get converted to LocalDate correctly.
Make sure that in MyCustomClass your toString() method prints correctly. My toString() method looks like this:
import java.time.LocalDate;
import org.json.JSONObject;
public class SimpleSearch {
private LocalDate startDate;
private LocalDate endDate;
// ...
// Getters and Setters for the LocalDate variables
// ...
#Override
public String toString() {
return new JSONObject(this).toString();
}
public SimpleSearch() {}
}
then your JSON printouts will always look like this when it gets sent to the lambda and not that other crazy Jackson format:
{
"startDate": "2018-11-01",
"endDate": "2018-11-16"
}
Some of the Maven dependencies I used:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.7</version>
</dependency>
Hopefully AWS fixes the Jackson conversions to be reciprocal, to and from JSON, so that we wouldn't have to resort to these custom conversions anymore.
create getter methods for the properties and put #JsonProperty on the getter methods.

Categories