I've made a generic method that is convertor class for complex classes and 2nd one for enums. I have Recipe class that is complex so I used #DynamoDBTypeConverted(converter = ObjectConverter.class)
This is my converter class:
public class ObjectConverter<T extends Object> implements DynamoDBTypeConverter<String, T> {
ObjectMapper objectMapper = new ObjectMapper();
#Override
public String convert(T object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
throw new IllegalArgumentException("Unable to parse JSON");
}
#Override
public T unconvert(String object) {
try {
T unconvertedObject = objectMapper.readValue(object, new TypeReference<T>() {
});
return unconvertedObject;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
This is convertor class for enums:
public class EnumConverter<T extends Object> implements DynamoDBTypeConverter<String, List<T>> {
#Override
public String convert(List<T> objects) {
//Jackson object mapper
ObjectMapper objectMapper = new ObjectMapper();
try {
String objectsString = objectMapper.writeValueAsString(objects);
return objectsString;
} catch (JsonProcessingException e) {
//do something
}
return null;
}
#Override
public List<T> unconvert(String objectsString) {
ObjectMapper objectMapper = new ObjectMapper();
try {
List<T> objects = objectMapper.readValue(objectsString, new TypeReference<List<T>>() {
});
return objects;
} catch (JsonParseException e) {
//do something
} catch (JsonMappingException e) {
//do something
} catch (IOException e) {
//do something
}
return null;
}
The problem is when I try to test CRUDs methods.. I have addProduct method and this one works fine, I created addRecipe method and it looks almost the same, but here I have problem while posting in Postman i got an error: "Bad request, unable to parse JSON".
And information from log file:
"Can not deserialize instance of java.util.ArrayList out of START_OBJECT token at [Source: {"id":null,"name":"test","labels":["GLUTEN_FREE"],"author":{"name":"Plejer Annołn","id":"testID2"},"media":{"name":"heheszki","url":"http://blabla.pl","mediaType":"IMAGE"},"recipeElements":{"product":{"id":927c3ed3-400b-433d-9da0-1aa111dce584,"name":"bąkiKacpraNieŚmierdzą","calories":1000,"fat":400.0,"carbo":20.0,"protein":40.0,"productKinds":["MEAT"],"author":{"name":"Plejer Annołn","id":"testID2"},"media":{"name":"heheszki","url":"http://blabla.pl","mediaType":"IMAGE"},"approved":false},"weight":"100"},"approved":false}; line: 1, column: 190] (through reference chain: pl.javamill.model.kitchen.Recipe["recipeElements"])"
What can be wrong?
The methods in the converter class are always returning a value even if exceptions are thrown (unless they are RuntimeExceptions), though they may not be correctly marshaling/unmarshaling the Product in RecipeElement. A better alternative is to annotate the getRecipeElement() method in your class with #DynamoDBTypeConvertedJson, that provides out-of-the-box JSON marshaling/unmarshaling. It may be something to do with the HTTP request you are sending in Postman too. You should add more information on the getProduct(), setProduct() methods and the actual postman request (without any sensitive information).
Related
I am trying to declare a method and a constructor to RestResponse class in case of the json returns a list of DTOs directly , or probably make the existing constructor smarter to differentiate if the rest response is a List or one single element.
public class RestResponse<T> implements IRestResponse<T> {
public static ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
private T data;
private Response response;
private Exception e;
// constructor
public RestResponse(Class<T> t, Response response) {
this.response = response;
try {
this.data = t.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("There should be a default constructor in the Response POJO");
}
// getBody method
public T getBody() {
try {
data = (T) response.getBody().as(data.getClass());
} catch (Exception e) {
this.e = e;
}
return data;
}
}
}
This implementation is very Ok when the return response is a single DTO like this:
{
"attribute1":"value1",
"attribute2":"value2",
"attribute3":"value3"
}
or even like this:
{
"attribute1":"value1",
"attribute2":"value2",
"attribute3":[
{ "att1":"val1",
"att2":"val2"
}]
}
but I can't get a generic solution to get a method when the return is like:
[
{
"attribute1":"value1",
"attribute2":"value2",
"attribute3":"value3"
},{
"attribute1":"value1",
"attribute2":"value2",
"attribute3":"value3"
}
]
I'm having trouble converting a List object and it's returning the error below:
expected S in value {L: [{M: {txt_just={S: new client,}, num_funl={S: 123,}, num_propt={S: 2f1a8e6c-68bb-4c26-9326-3823d9f96c4c,}, ind_decs={S: S,}, dat_hor={S: 20220721183000,}},}],}
My Class translator:
public class ObjectTranslators<T> implements DynamoDBTypeConverter<String, T> {
private static final ObjectMapper mapper = new ObjectMapper();
#Override
public String convert(T t) {
try {
return mapper.writeValueAsString(t);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Unable to parse JSON");
}
}
#Override
public T unconvert(String s) {
try {
return mapper.readValue(s, new TypeReference<>() {});
} catch (IOException e) {
throw new IllegalArgumentException("Unable to read JSON");
}
}
}
You are trying to convert a List that has a single entry which is a Map of 5 key/value pairs. Your real issue is probably that there is no value supplied to ind_decs but, it's also possible that you aren't properly using the expected generic T type wherever ObjectTranslators is used as well.
expected S in value
{
L: [
{
M: {
txt_just={S: new client,},
num_funl={S: 123,},
num_propt={S: 2f1a8e6c-68bb-4c26-9326-3823d9f96c4c,},
ind_decs={S: S,},
dat_hor={S: 20220721183000,}
},
}
],
}
public <T> void getJsonObject(String res, RequestCallBack<T> callBack) {
T result;
if (res != null) {
ObjectMapper mapper = new ObjectMapper();
try {
result = mapper.readValue(res, new TypeReference<T>() {});
callBack.onSuccess(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
it can't transmit T to the mapper the result is not what i want, what should I do?
A lot of libraries get around the type erasure by mandating the caller to pass the class in.
public <T> void getJsonObject(String res, RequestCallBack<T> callBack, Class<T> clazz) {
T result;
if (res != null) {
ObjectMapper mapper = new ObjectMapper();
try {
result = mapper.readValue(res, clazz);
callBack.onSuccess(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
On a side note, you probably shouldn't be creating a new ObjectMapper for each call (it can be an expensive operation). I would suggest creating a single static one.
I have problem with REST service I'm trying to make. I use GlassFish 4.1 and Jersay 2.1 which is built-in.
#Path("/driver")
#RequestScoped
public class DriverResource {
private static Logger logger = LogManager.getLogger(DriverResource.class);
#Inject
private DriverManager driverManager;
private SharedResponseFactory responseFactory = new SharedResponseFactory();
#GET
#Path("/login/{pesel}/{password}")
#Produces("application/json")
public Response logIn(#PathParam("pesel") String pesel, #PathParam("password") String password) {
try {
Driver driver = driverManager.logIn(pesel, password);
logger.debug("Zalogowano kierowcę: " + driver.getFullName());
return responseFactory.getSuccesResponse(driver);
} catch (ErrorDAOException e) {
logger.catching(e);
return responseFactory.getFailureResponse(e);
} catch (NoDataFoundDAOException e) {
logger.catching(e);
return responseFactory.getFailureResponse(e);
} catch (Exception e) {
logger.catching(e);
return responseFactory.getFailureResponse(e);
}
}
}
When I'm trying to return my Entity I get response like this:
{}
In my Entities there are many cyclic references and I don't operate on actual implementation but interfaces. I need to make it the way Retrofit in my Android application could deserialize it.
Glassfish's logs are empty, there are no errors related to rest. I have no idea how to make it working.
I tried to use #JsonIdentityInfo to handle cyclic references and #JsonTypeInfo to make interfaces possible to desserialize.
I think there's a small trick that will make it working but unfortunately I don't know it...
I found a relatively easy way to test for cyclic references. If you use JAXB (which is included with glassfish), you can try marshalling your entity to XML. A JAXBException is thrown if any cyclic references are found.
Here is a method to marshall an object to XML:
public static <T> String marshalToXml(T instance) throws javax.xml.bind.JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(instance.getClass());
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
jaxbMarshaller.marshal(instance, writer);
return writer.toString();
}
and the service can test the object like this:
#Path("/driver")
#RequestScoped
public class DriverResource {
private static Logger logger = LogManager.getLogger(DriverResource.class);
#Inject
private DriverManager driverManager;
private SharedResponseFactory responseFactory = new SharedResponseFactory();
#GET
#Path("/login/{pesel}/{password}")
#Produces("application/json")
public Response logIn(#PathParam("pesel") String pesel, #PathParam("password") String password) {
try {
Driver driver = driverManager.logIn(pesel, password);
marshalToXml(driver); //remember to import static method
logger.debug("Zalogowano kierowcę: " + driver.getFullName());
return responseFactory.getSuccesResponse(driver);
} catch (ErrorDAOException e) {
logger.catching(e);
return responseFactory.getFailureResponse(e);
} catch (NoDataFoundDAOException e) {
logger.catching(e);
return responseFactory.getFailureResponse(e);
} catch (JAXBException e) {
//view error message to see cyclic reference
logger.catching(e);
return responseFactory.getFailureResponse(e);
} catch (Exception e) {
logger.catching(e);
return responseFactory.getFailureResponse(e);
}
}
}
I'm sorry it was surely already asked, but I did not find what I need
I have several message types:
class AbstractMessage {
int code;
String token;
}
class ShareMessage extends AbstractMessage{
String user;
Map<String, String> friends;
}
class PostMessage extends AbstractMessage{
String user;
Map<String, String> data;
}
and a method to decode them from the json post message:
public Object getMessage(BufferedReader r, Type t){
Object o = null;
try{
o = g.fromJson(r, t);
} catch (final JsonSyntaxException e) {
LOGGER.info("Error in Json format", e);
} catch (final JsonParseException e) {
LOGGER.info("Error in parsing Json", e);
}
return o;
}
then for example:
Type dataType = new TypeToken<PostMessage>() {}.getType();
PostMessage m = (PostMessage) getMessage(request.getReader(), dataType);
works, but it's ugly, how can I have a parametrized getMessage function, or anything better than returning Object and casting
thx
Add <T> to the method signature, immediately before the return type. This creates a type-parameterized method:
public <T> T getMessage(BufferedReader r, TypeToken<T> typeToken){
try {
return g.fromJson(r, typeToken.getType());
} catch (final JsonSyntaxException e) {
LOGGER.info("Error in Json format", e);
} catch (final JsonParseException e) {
LOGGER.info("Error in parsing Json", e);
}
return null;
}
Call it like this:
PostMessage m = getMessage(request.getReader(), new TypeToken<PostMessage>() {});