Cannot pass string to AWS Lambda using InvokeRequest.withPayload() method - json

I have a lambda function that accepts a String as an input parameter. When running the lambda function I get the following error:
Can not deserialize instance of java.lang.String out of START_OBJECT token\n
This is what my code too call it looks like:
InvokeRequest request = new InvokeRequest();
final String payload = "";
request.withFunctionName(FUNCTION_NAME).withPayload((String) null);
InvokeResult invokeResult = lambdaClient.invoke(request);
Assert.assertEquals(new String (invokeResult.getPayload().array(), "UTF-8"), "Success");
And this is what my handler looks like:
public String handleRequest(String s, Context context) {}
Now the contents of the string don't matter, it could be null it could be anything. I don't use the input. The obvious solution is to remove it, but because of an annotation generator i'm using I can't do that. I've tried a ByteBuffer input, String, empty String, JSON String {\"s\":\"s\"} but nothing seems to work. I believe I need to pass in a string (i.e no {}). But since I'm using InvokeRequest I don't believe I can do that. Any suggestions would be greatly appreciated.

It works by passing a JSON valid String.
String payload = "{ \"subject\" : \"content\"}";
request.withFunctionName(functionName)
.withPayload(payload);
At the receiving end you have to map it from Object to String again if that's what you want. Here I used Jackson ObjectMapper for this.
ObjectMapper objectMapper = new ObjectMapper();
try {
String payload = objectMapper.writeValueAsString(input);
} catch (JsonProcessingException e) {
e.printStackTrace();
}

Related

String object is not making it from the Controller to the Razor Component

I have a Controller that defines a number of Get methods. The Razor Component is able to read all the Gets using GetFromJsonAsync() without any issue. However, one of the Get methods returns a string object. The Razor Component keeps blowing up when trying to read that string.
MyController.cs
[HttpGet("GetObject")]
public async Task<MyObject> GetObject(int? id)
{
MyObject obj = new MyObject();
// ... do some work here and fill in obj
return obj;
}
[HttpGet("GetString")]
public async Task<string> GetString(int? id)
{
string retval = "";
// ... do some work here and fill in retval
return retval;
}
MyComponent.razor
// this call works
MyObject myObj = await _http.GetFromJsonAsync<string>("My/GetObject?id=15");
// this call throws a Json syntax error
string myString = await _http.GetFromJsonAsync<string>("My/GetString?id=15");
I'm not sure why the system is able to read all my objects with the exception of the string object. Has anyone else run into this issue with being unable to read strings from the Controller?
I finally figured out what is going on. It appears that if your Controller is returning a string object, Blazor is assuming that you have already converted your data into a true Json string and will not attempt to serialize it into Json for you. So I had to serialize it myself on the Controller side. Then my Razor Component picked up the original string as intended. Here is the solution:
[HttpGet("GetString")]
public async Task<string> GetString(int? id)
{
string retval = "";
// ... do some work here and fill in retval
return System.Text.Json.JsonSerializer.Serialize<string>(retval);
}

How to handle two data types coming in same response for same parameter name

I am calling a third-party API that returns two different values for the same parameter name as below,
ActivationResponse activationResponse = new ActivationResponse();
ResponseEntity<ActivationResponse> response = null;
response = restTemplate.exchange(Url, HttpMethod.POST, request, ActivationResponse.class);
activationResponse = response.getBody();
Error response:
{
"executionCode":"2",
"executionMessage":"NullPointerException Occured!",
"response":"java.lang.NullPointerException"
}
Success response:
{
"executionCode" : "0",
"executionMessage" : "SUCCESS",
"response" : {
"orderID" : "79966036"
}
}
As the sample response response parameter can come as a string or with a JSON object.
Currently the response model is as below,
public class ActivationResponse {
private String executionCode;
private String executionMessage;
private ActivationResponseDetails response;
}
public class ActivationResponseDetails {
private String orderID;
}
When the error comes, an exception is thrown indicating that it can't handle the response parameter. Please advice how to handle both success and failure scenarios without issues.
Please Note that approach in this answer is not possible, because I have to print the logs in following way, so using #JsonIgnore will not show that parameter on the log.
Logs are printed like this,
log.info("ActivationResponse json : {}", mapper.writerWithDefaultPrettyPrinter().writeValueAsString(response.getBody()));
If you insist on having a one-size-fits-all Response object, setting the type of the response field to Object, may do the trick:
public class ActivationResponse {
private String executionCode;
private String executionMessage;
private Object response;
}
The field 'response' should now resolve to a simple String (success) or a more involved Java object (error).

jackson reading in non-existent and null values to “” and marshalling out “” to non-existent values?

I read through this post Jackson: deserializing null Strings as empty Strings which has this cool trick
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
THEN on the flipside, I read through this post Jackson serialization: ignore empty values (or null) which has this cool trick
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
This is VERY VERY close except I really don't want incoming data to be null in any case. I have the following code printing 4 situations with the above settings BUT want to fix the null piece so any json we unmarshal into java results in
public class MapperTest {
private static final Logger log = LoggerFactory.getLogger(MapperTest.class);
private ObjectMapper mapper = new ObjectMapper();
public MapperTest() {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
}
public static void main(String[] args) throws JsonProcessingException {
new MapperTest().start();
}
private void start() throws JsonProcessingException {
//write out java color=null resulting in NO field...
String val = mapper.writeValueAsString(new Something());
log.info("val="+val);
Something something = mapper.readValue(val, Something.class);
log.info("value='"+something.getColor()+"'");
//write out java color="" resulting in NO field...
Something s = new Something();
s.setColor("");
String val2 = mapper.writeValueAsString(new Something());
log.info("val="+val2);
String temp = "{\"color\":null,\"something\":0}";
Something something2 = mapper.readValue(temp, Something.class);
log.info("value2='"+something2.getColor()+"'");
}
}
The output is then
INFO: val={"something":0}
INFO: value='null'
INFO: val={"something":0}
INFO: value2=''
NOTE: The value = 'null' is NOT what I desire and want that to also be empty string. Notice that if customers give a color:'null', it does result in empty string. Non-existence should result in the same thing for us "".
This is a HUGE win in less mistakes in this area 'for us' I mean.
thanks,
Dean

Spring - Return Raw JSON without double serialization

I know there are other posts similar to this, but I haven't found any that help me find a solution for this particular case.
I am trying to return a HashMap<String, Object> from my Controller.
The Object part is a JSON string, but its being double serialized and not returned as a raw JSON string, thus not ending up with extra quotations and escape characters.
Controller function:
#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public HashMap<String, Object> heartbeat(){
String streamInfo = service.getStreamInfo();
String streamCursorInfo = service.getStreamCursorInfo();
String topicInfo = service.getTopicInfo();
String greeting = "This is a sample app for using Spring Boot with MapR Streams.";
HashMap<String, Object> results = new HashMap();
results.put("greeting", greeting);
results.put("streamInfo", streamInfo);
results.put("streamCursorInfo", streamCursorInfo);
results.put("topicInfo", topicInfo);
return results;
}
Service function:
private String performCURL(String[] command){
StringBuilder stringBuilder = new StringBuilder();
try{
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process p = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while((line = reader.readLine()) != null){
stringBuilder.append(line);
}
}
catch(Exception e){
LOGGER.error(ExceptionUtils.getRootCauseMessage(e));
}
return stringBuilder.toString();
}
The cURL command I run already returns a raw JSON string. So im just trying to add it to the HashMap to be returned in the heartbeat response.
But every time I run this, my output looks like:
{
"greeting": "This is a sample app for using Spring Boot with MapR Streams.",
"streamCursorInfo": "{\"timestamp\":1538676344564,\"timeofday\":\"2018-10-04 02:05:44.564 GMT-0400 PM\",\"status\":\"OK\",\"total\":1,\"data\":[{\"consumergroup\":\"MapRDBConsumerGroup\",\"topic\":\"weightTags\",\"partitionid\":\"0\",\"produceroffset\":\"44707\",\"committedoffset\":\"10001\",\"producertimestamp\":\"2018-10-03T05:57:27.128-0400 PM\",\"consumertimestamp\":\"2018-09-21T12:35:51.654-0400 PM\",\"consumerlagmillis\":\"1056095474\"}]}",
...
}
If i return only the single string, such as streamInfo then it works fine and doesnt add the extra quotes and escape chars.
Can anyone explain what im missing or need to do to prevent this double serialization?
Instead of returning a HashMap, create an object like this:
public class HeartbeatResult {
private String greeting;
... //other fields here
#JsonRawValue
private String streamCursorInfo;
... //getters and setters here (or make the object immutable by having just a constructor and getters)
}
With #JsonRawValue Jackson will serialize the string as is. See https://www.baeldung.com/jackson-annotations for more info.
streamCursorInfo is a string, not an object => the serialization will escape the " character.
If you are able to return the object containing the data, it will work out of the box. If what you have is just a String, I suggest to serialize it to JsonNode and add it in your response
ObjectMapper objectMapper = new ObjectMapper();
JsonNode streamCursorInfo = objectMapper.readTree(service.getStreamInfo())
results.put("streamCursorInfo", streamCursorInfo);

jackson deserialize Map object inside List

I have the following JSON in a file
[
{"numberEnrolledPerMonthPerWeek":
{
{"year":"2011","numberEnrolled":0,"weeks":2},
{"year":"2011","numberEnrolled":0,"weeks":3},
{"year":"2011","numberEnrolled":0,"weeks":4},
{"year":"2011","numberEnrolled":0,"weeks":5},
{"year":"2011","numberEnrolled":0,"weeks":6},
{"year":"2011","numberEnrolled":0,"weeks":7},
{"year":"2011","numberEnrolled":0,"weeks":8},
{"year":"2011","numberEnrolled":0,"weeks":9}
}
,"country":"Argentina"
},
]
When I use Jackson to deserialise this into a Java object I get the following error
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token
i am using the following code
ObjectMapper mapper = new ObjectMapper();
List<EnrolledEnrolment> enrolments = mapper.readValue(new File("src/main/resources/data/jsonQueriesTestData1.txt"),
new TypeReference<List<EnrolledEnrolment>>(){});
I have used typeReference for the initial array but do how do I use type Reference for the hashmap inside the object EnrolledEnrolment.
private Map<Integer, Enrolled> numberEnrolledPerMonthPerWeek = new HashMap<Integer,Enrolled>();
The error is thrown when it tries to parse the 2nd Array? Any ideas
Thanks
Hard to tell from the lack of an actual error in the original post, but I encountered an issue when trying to automatically deserialize JSON to an Object where the JSON contain a Map within a List.
I was seeing this error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `java.util.LinkedHashMap` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value
I fixed it be adding #JsonDeserialize(converter = JsonToMapConverter.class) to my Map in my POJO.
Where that class looked something like this
public class JsonToMapConverter extends StdConverter<String, Map<String, MyPojo>> {
#Override
public Map<String, MyPojo> convert(String value) {
try {
return JsonUtil.convertJsonToMap(value, String.class, MyPojo.class);
}
catch (Exception e) {
throw new RuntimeException(String.format("Failed to convert String to POJO [%s]", value));
}
}
}
With the utility method looking like
public static <K,V> Map<K,V> convertJsonToMap(String json, Class<K> key, Class<V> value) throws IOException {
JavaType type = mapper.getTypeFactory()
.constructMapLikeType(Map.class, key, value);
return mapper.readValue(json.getBytes(StandardCharsets.UTF_8), type);
}