6

I'm new to Java so this may not be related to AWS lambda at all. However, lambda takes such liberties with input/output objects that I'm assuming it's the culprit here.

I'm building my first lambda function and want to return a simple JSON structure (simplified further for this example):

{
  "document" : "1",
  "person" : { "name" : "John Doe" }
}

However, when lambda serializes the JSON it always sets "person" to a blank object!

{
  "document": "1",
  "person": {}
}

Here is my code in full:

 - test1.java
package handler_test;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class test1 implements RequestHandler<String, ResponseClass> {
    @Override
    public ResponseClass handleRequest(String input, Context context) {
      return new ResponseClass();
    }
}

 - ResponseClass.java
package handler_test;
import org.json.JSONException;
import org.json.JSONObject;
public class ResponseClass {
    String document;
    JSONObject person;

    public String getdocument() {
        return "1";
    }

    public JSONObject getperson() {
        try {
            return new JSONObject("{ \"name\" : \"John Doe\" }");
        } catch (JSONException e1) {
            System.out.println("error creating jsonobject");
            return null;
        }
    }

    public ResponseClass() {
    }
}

I've tried this with dozens of variations, using objects instead of JSONObjects, converting getperson output to a string (which works, if I wanted a string), etc. If I store the values and print them to the logger, it's fine. But as soon as I try to return it through lambda it goes pear-shaped. I've combed the 'net and can't find anything more on AWS-lambda java response objects beyond Amazon's "greetings" sample code, which just includes two strings. Any suggestions greatly appreciated!

4
  • Refer this docs.aws.amazon.com/lambda/latest/dg/…, docs.aws.amazon.com/lambda/latest/dg/… Commented Apr 13, 2016 at 13:36
  • Also this docs.aws.amazon.com/lambda/latest/dg/… Commented Apr 13, 2016 at 13:38
  • Thanks. That's the "greetings" sample code I referred to, which only returns a simple object. AWS doesn't have any samples of returning anything more complex. They do mention that if the out of box serialisation doesn't work for you, you can write your own JSON serialisation but don't say how to do that! Being new to java and lambda, I'm at a loss. (And frankly, returning a JSON object inside a JSON object does not seem like such an unusual request that I should have to roll my own tools, although no one else seems to be doing it in lambda.) Commented Apr 14, 2016 at 2:03
  • Hey @Gregg Have you got proper solution about this question ? if yes please share here. I also want answer for this question. Commented May 18, 2016 at 9:48

3 Answers 3

5

I solved this using the stream handlers, and not only does it work but you have more control and less code! I used gson for JSON serialization/deserialization and the Apache IOUtils for converting inputsteam to a string. As I'd already written it using the Request and Response classes, I continued using those, although I was able to get rid of all the getter and setter code.

Two notes: 1. gson will output all non-null attributes of the Response class, even if they're declared private, so if there are values you don't want to spit back be sure to set them to null before the final line. 2. When using Eclipse IDE with the AWS plugin, it will not upload the code to AWS unless it can find a RequestHandler! Thus I have a stub function that is immediately overridden.

import com.google.gson.*;
import org.apache.commons.io.IOUtils;
import com.amazonaws.services.lambda.runtime.*;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;

public class Intel implements RequestStreamHandler, RequestHandler<Object, Object> {
    @Override
    public Object handleRequest(Object input, Context context) {
        return null;
    }

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
        Request req = new Gson().fromJson(IOUtils.toString(inputStream, "UTF-8"), Request.class);
        Response resp = new ResponseClass();
            resp.id = 1;
            resp.person.name = req.name;
            et_home_phone(req.name);
        outputStream.write(new Gson().toJson(resp).getBytes(Charset.forName("UTF-8")));
    }

    private void get_home_phone(String name) {
        // call external API -- stub example!  Assumes only one phone returned
        // in the format { "number" : "123-456-7890", "type" = "home" }
        // gson magic assures they get copied to the keys of the same name
        HttpGet httpGet = new HttpGet(phoneURL + "/" + name));
        HttpResponse httpResponse = client.execute(httpGet);
        resp.phone[0] = new Gson().fromJson(IOUtils.toString(httpResponse .getEntity().getContent(), "UTF-8"), Response.Phone.class);
    }
}

public class Response {
    public class Person {
        String name;
    }
    public class Phone {
        String number;
        String type;
    }
    public Integer id;
    public Person person = new Person();
    public Phone[] phone = new Phone[5];
}
Sign up to request clarification or add additional context in comments.

2 Comments

I think you should accept your own answer. That is allowed AFAIK.
You really should accept your own answer. This is a great solution. One thing to add - You don't need to implement RequestHandler<> This way you can remove the first handleRequest method completely
2

You can skip JSONObject and use a POJO for the nested class. However, naming according to conventions is important here. Make sure your accessor methods are named using camel case (get + name of property capitalized). Try this:

public class ResponseClass {
    String document;
    Person person;

    public String getDocument() {
        return "1";
    }

    public Person getPerson() {
        return person;
    }
}

class Person {
    String name;
    public String getName() {
        return name;
    }
}

2 Comments

Unfortunately, that doesn't create a nested object, only a top-level key; eg: { "document": "1", "person": "john doe" } If I change Person class to return a JSON object (which is what I want) I get the same results: an empty "person" object. { "document": "1", "person": {} }
@Gregg: Faced similar issue. Defining default constructor in POJO will solve this.
0

From the documentation: https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html

"If it's a type that wraps a primitive value, the runtime returns a text representation of that value."


So the simple person class with a single string attribute will return something like:

{ "document": "1", "person": "john doe" }

Add another field to the person class and it will work as desired:

class Person {
    String firstName;
    String lastName;
    ...
}

Output:

{ "document": "1", "person": { "firstName": "John", "lastName": "Doe" }}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.