Converting POJO to JsonNode using a JsonView - java

I'm writing a typical Play Framework app where I want to return a JsonNode from my Controller's methods, using Jackson.
This is how I'm doing it right now:
public static Result foo() {
MyPojoType myPojo = new myPojo();
String tmp = new ObjectMapper().writerWithView(JSONViews.Public.class).writeValueAsString(myPojo);
JsonNode jsonNode = Json.parse(tmp);
return ok(jsonNode);
}
Is it possible to avoid the "String tmp" copy and convert directly from MyPojoType to JsonNode using a view?
Maybe I can use ObjectMapper.valueToTree, but I don't know how to specify a JSonView to it.

Interesting question: off-hand, I don't think there is a specific method, and your code is the most straight-forward way to do it: valueToTree method does not apply any views.
So code is fine as is.

After more investigation, this is what I did in the end to avoid the redundant work:
public Result toResult() {
Content ret = null;
try {
final String jsonpayload = new ObjectMapper().writerWithView(JsonViews.Public.class).writeValueAsString(payload);
ret = new Content() {
#Override public String body() { return jsonpayload; }
#Override public String contentType() { return "application/json"; }
};
} catch (JsonProcessingException exc) {
Logger.error("toResult: ", exc);
}
if (ret == null)
return Results.badRequest();
return Results.ok(ret);
}
In summary: The methods ok, badRequest, etc accept a play.mvc.Content class. Then, simply use it to wrap your serialized json object.

As i know, with jax-rs, you can do this :
public Response toResult() throws JsonProcessingException {
final ObjectWriter writer = new ObjectMapper()
.writerWithView(JSONViews.Public.class);
return Response.ok(new StreamingOutput() {
#Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
writer.writeValue(outputStream, /*Pojo*/ payload);
}
}).build();
}
So you have to find a class in the Play framework which able to stream the result (through an OutputStream)

I think this is more efficient way
public Result toResult() {
MyPojo result = new MyPojo();
JsonNode node = objectMapper.valueToTree(result);
return ok(node);
}

Related

Jackson deserialize Double

I have a problem with deserialization of the JSON using Jackson in my rest-assured tests.
In my JSON I have a key "value" that can be an array of Strings or object like Boolean.
{
"value": ["value1", "value2"]
}
or
{
"value": 2272204.2426
}
So I wrote custom deserializer for this field:
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
if (node.isArray()) {
List<String> list = new ArrayList<>();
for (JsonNode elementNode : node) {
list.add(oc.treeToValue(elementNode, String.class));
}
return list;
} else {
if(node.isDouble()) {
return oc.treeToValue(node, Double.class);
}
else if(node.isBoolean()){
return oc.treeToValue(node, Boolean.class);
}
else {
return oc.treeToValue(node, String.class);
}
}
}
In the end I've noticed that numeric value like 2272204.2426 is deserialized to 2272204.2
I tried to desierialize it using Gson and it works well. Do you have any idea why using Jackson there is lack of decimal part?
I've tried to debug the code and I've noticed that on this step JsonNode node = oc.readTree(jp); the value is 2272204.2
Why not use ObjectMapper from Jackson? You can add DeserializationFeature to it unlike ObjectCodec. Mapper is actually extending Codec, but with more features that you need in this case.
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
JsonNode node = //node where the value is defined as Double
Double value = null;
try {
value = mapper.treeToValue(node, Double.class);
}
catch (IOException e) {
e.printStackTrace();
}
System.out.println(value);
Use the above logic in your node.isDouble() case

nested generic parameter as a method parameter - Java

Based on this question How to get a class instance of generics type T I have implemented the following class:
public class OkJsonConverter<T> {
final Class<T> typeParameterClass;
public OkJsonConverter(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}
protected T processJson(String json) throws OkClientException {
T object = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode jsonNode = objectMapper.readTree(json);
if (jsonNode.get("error_code") != null) {
Error error = objectMapper.treeToValue(jsonNode, Error.class);
throw new OkClientException("API returned error", error);
} else {
object = objectMapper.treeToValue(jsonNode, typeParameterClass);
}
} catch (IOException e) {
throw new OkClientException("unable to process json", e);
}
return object;
}
}
I can use this class with a generic parameters, for example:
return new OkJsonConverter<User>(User.class).processJson(response.getBody());
but right now I'm struggling how to make it working with a nested generic parameter like this one List<Method>
This code doesn't work:
return new OkJsonConverter<List<Method>>(List<Method>.class).processJson(response.getBody());
Please help to change this code in order to get it working.
Java doesn't have any way to represent that type as a Class. The closest approximation you can get is (Class<List<Method>>) (Class) List.class, but that cast just papers over that you're just looking at a basic List that doesn't know its element type.
Whether or not that works with your JSON converter isn't clear, but should be specified in the documentation of the converter you're using, which will have to deal with this itself, since this is a universal problem in Java when you're trying to reflect on generic types.
Finally, thanks to user3707125 I have found a way how to implement this:
Corrected by idierL
public class OkJsonConverter {
private static final String ERROR_CODE_FIELD_NAME = "error_code";
protected <T> T readTreeToValue(String json, TypeReference<T> valueTypeRef) throws OkClientException {
T object = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode jsonNode = objectMapper.readTree(json);
if (jsonNode.has(ERROR_CODE_FIELD_NAME)) {
Error error = objectMapper.treeToValue(jsonNode, Error.class);
throw new OkClientException("Ok API returned error", error);
} else {
JavaType type = objectMapper.getTypeFactory().constructType(valueTypeRef);
object = objectMapper.convertValue(jsonNode, type);
}
} catch (IOException e) {
throw new OkClientException("Unable to process JSON", e);
}
return object;
}
}
Now, the following code works fine:
List<Method> result = new OkJsonConverter().readTreeToValue(response.getBody(), new TypeReference<List<Method>>() {});

Discrepancy in JSON ObjectMapper readValue parsing - gives Null sometimes

I am facing a bizarre situation while parsing a json string using readValue() of ObjectMaper. I am using Jackson 2.4.1 along with Spring 4.0.6.
Problem is, same json string when fed to readValue() gives null at times and other times valid object.
JSON String :
{"productGroupInfoTransport": {"groupId":36,"range":"LMEURSMA","productType":"LMFE","status":"ANNOUNCE","regionsList":[],"productsList":[],"groupName":"Bright Start test"}}
Code:
public Object getData(String jSONObject, String action, String module)
throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES,
false);
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT,
false);
//mapper.configure(SerializationConfig. WRITE_NULL_PROPERTIES, false);
String transport = null;
if (jSONObject.contains(ERROR)) {
transport = "Exception";
throw new Exception("Unable to read Data");
} else {
transport = getTransport(action, module);
}
Object transportObject = null;
transportObject = mapper.readValue(jSONObject, getTransport(transport));
return transportObject;
}
ProductTransport :
package com.leggmason.lmEPAM.transport;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.leggmason.lmEPAM.model.ProductGroupModel;
public class ProductGroupTransport extends EPAMTransport<ProductGroupModel> {
#JsonProperty("productGroupInfoTransport")
protected ProductGroupModel modelObject;
#JsonProperty("productGroupTransport")
protected List<ProductGroupModel> data;
#JsonProperty("productGroupTransport")
#Override
public List<ProductGroupModel> getListData() {
return this.data;
}
#JsonProperty("productGroupTransport")
#Override
public void setData(List<ProductGroupModel> data) {
this.data = (List<ProductGroupModel>) data;
}
#Override
public String getModule() {
return null;
}
#JsonProperty("productGroupInfoTransport")
#Override
public ProductGroupModel getObject() {
return this.modelObject;
}
#JsonProperty("productGroupInfoTransport")
public void setObject(ProductGroupModel modelObject) {
this.modelObject = modelObject;
}
#Override
public void setObject(Object modelObject) {
// TODO Auto-generated method stub
}
}
In above code when JsonString is passed, one time ProductTransposrt will have its modelObject populated correctly with Object but as I restart the server, same JSON string will give modelObject as null after parsing. I fail to understand why. Thanks for help.
You can try following code for parsing json object
JSONObject obj = new JSONObject(jSONObject);
JSONArray array = obj.getJSONArray("productGroupInfoTransport");
System.out.println(array.getJSONObject(0).getString("groupId"))
Now you can parse json string properly.
It may be help for only parsing json string.

RestTemplate & Jackson - Custom JSON deserializing?

The webservice returns an empty string instead of NULL which causes Jackson to crash.
So I created a custom parser, and I'm trying to parse it manually? Any idea How I could achieve this?
What Am I doing wrong here? All I'm trying to do is to parse JSON to object as I normally would. The field names are added to my properties using #JsonProperty so the parser should know how to convert it.
public class InsertReplyDeserializer extends JsonDeserializer<ListingReply> {
#Override
public ListingReply deserialize(JsonParser jsonParser, DeserializationContext arg1)
throws IOException, JsonProcessingException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
// If service returns "" instead of null return a NULL object and don't try to parse
if (node.getValueAsText() == "")
return null;
ObjectMapper objectMapper = new ObjectMapper();
ListingReply listingReply = objectMapper.readValue(node, ListingReply.class);
return listingReply;
}
}
Here is how I resolved it
#Override
public MyObject deserialize(JsonParser jsonParser, DeserializationContext arg1)
throws IOException, JsonProcessingException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
if (node.getValueAsText() == "")
return null;
MyObject myObject = new MyObject();
myObject.setMyStirng(node.get("myString").getTextValue());
JsonNode childNode = node.get("childObject");
ObjectMapper objectMapper = new ObjectMapper();
ChildObject childObject = objectMapper.readValue(childNode,
ChildObject.class);
myObject.setChildObject(childObject);
return myObject;
}
I am not sure you need to manually parse response. You solution would work but seems sub-optimal in my opinion. Since it looks like that you are using RestTemplate, you should rather write (or move your parser code to) your own message converter. Then add this converter to your rest template object which will internally deserialize the value for you. Something along the lines,
public class CustomHttpmsgConverter extends AbstractHttpMessageConverter<Object> {
private ObjectMapper objectMapper = new ObjectMapper();
#Override
protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
InputStream istream = inputMessage.getBody();
String responseString = IOUtils.toString(istream);
if(responseString.isEmpty()) //if your response is empty
return null;
JavaType javaType = getJavaType(clazz);
try {
return this.objectMapper.readValue(responseString, javaType);
} catch (Exception ex) {
throw new HttpMessageNotReadableException(responseString);
}
}
//add this converter to your resttemplate
RestTemplate template = new RestTemplate();
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
converters.add(new CustomHttpmsgConverter());
template.setMessageConverters(converters);

Jackson custom deserializers creating empty pojo when reading list

I have the following json
{
"id":null,
"name":"Myapp",
"description":"application",
"myListA":["java.util.ArrayList",[{
"id":50,
"name":"nameA1",
"myListB":{
"id":48,
"name":"nameB1",
"myListC":["java.util.ArrayList",[{
"id":1250,
"name":"nameC1",
"description":"nameC1_desc",
"myReferenceObject":{
"code":"someCodeA"
}
},{
"id":1251,
"name":"nameC2",
"description":"nameC1_desc",
"myReferenceObject":{
"code":"someCodeB"
}
and so on.
I i wish to replace myReferenceObject with an item from persistence layer.
I followed JacksonHowToCustomDeserializers
My deserializer is as follows:
public class MyReferenceObjectCodeDeserializer extends JsonDeserializer<MyReferenceObjectBean> {
#Override
public ColumnReferenceBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jp.getCurrentName();
jp.nextToken();
if ("code".equalsIgnoreCase(fieldname)) {
MyReferenceObjectBean b = MyReferenceObjectServiceImpl.retrieveByCode(jp.getText());
logger.info("returning " +b.toString());
return b;
}
}
logger.info("returning null");
return null;
}
}
And I attache the module like so:
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
SimpleModule module = new SimpleModule("myModule", new Version(1, 0, 0, null));
module.addDeserializer(MyReferenceObjectBean.class, new MyReferenceObjectCodeDeserializer());
mapper.registerModule(module);
try {
return mapper.readValue(serializedJsonString, MyMainObjectBean.class);
} catch (IOException e) {
logger.error("Unable to parse=" + serializedJsonString, e);
}
everything debugs correctly however the resulting myListC list has double the amount of objects with even numbers holding the correct objects along with the correct myReferenceObject out of persistence (deserialized correctly using my module) and the odd elements holding empty Pojos, that is an object with null values for all variables.
Through debug, It never reaches return null in my custom deserializer, for it works properly each time its executed. The issue seems to be further up stream where it inserts blank myListC objects.
Any help would greeatly be appreciated.
Thanks!
There is a logic problem in your code.
You want to loop until you reach the end of the object but break your loop with return b (if block). This means that you will not read the object stream until its end.
Try something like this (didn't try it but should work).
public class MyReferenceObjectCodeDeserializer extends JsonDeserializer<MyReferenceObjectBean> {
#Override
public ColumnReferenceBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
MyReferenceObjectBean b = null;
while (jp.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jp.getCurrentName();
jp.nextToken();
if ("code".equalsIgnoreCase(fieldname)) {
b = MyReferenceObjectServiceImpl.retrieveByCode(jp.getText());
logger.info("returning " +b.toString());
}
}
if (b==null) logger.info("returning null");
return b;
}
}
You can also have a look at Genson http://code.google.com/p/genson/ if you can change from jackson. In addition of some other features it is supposed to be easier to use. Here is how you can solve your problem with genson (for this example it is quite similar to jackson):
public class MyReferenceObjectCodeDeserializer implements Deserializer<MyReferenceObjectBean> {
public MyReferenceObjectBeandeserialize(ObjectReader reader, Context ctx) throws TransformationException, IOException {
MyReferenceObjectBean b = null;
reader.beginObject();
while (reader.hasNext()) {
reader.next();
if ("code".equalsIgnoreCase(reader.name()))
b = MyReferenceObjectServiceImpl.retrieveByCode(reader.valueAsString());
}
reader.endObject();
return b;
}
}
// register it
Genson genson = new Genson.Builder().withDeserializers(new MyReferenceObjectCodeDeserializer()).create();
MyClass myClass = genson.deserialize(json, MyClass.class);

Categories

Resources