Discrepancy in JSON ObjectMapper readValue parsing - gives Null sometimes - java

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.

Related

Gson: Expected begin_array but was STRING how to control this

Im learning how to produce and consume JSON in rest services, but I wanna learn it well so im trying all possible cases of objects, one of them is an object that has an List attribute like this class:
import java.util.List;
public class PruebaJSON {
private String nombre;
private List atributos;
private String descripcion;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public List getAtributos() {
return atributos;
}
public void setAtributos(List atributos) {
this.atributos = atributos;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}
Then all what im doing on my rest service method is this:
#POST
#Path("/prueba")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public PruebaJSON prueba(String data) {
try {
JSONObject json = new JSONObject(data);
Gson convertir = new GsonBuilder().create();
PruebaJSON pruebaJson = convertir.fromJson(json.toString(), PruebaJSON.class);
return pruebaJson;
} catch (Exception e) {
System.out.println("error " + e);
return null;
}
}
Then in POSTMAN I pass this:
{
"descripcion": "Primera prueba",
"nombre": "Prueba 1",
"atributos": [
"hello",
"kek",
"lul"
]
}
And it works fine, the problem is when I try to do the same by Java, for example:
List atributos = new ArrayList<>();
atributos.add("hello");
atributos.add("kek");
atributos.add("lul");
System.out.println(bus.prueba("Prueba 1", "Primera Prueba", atributos));
bus.prueba just executes the service but then in console I get this error:
14:16:56,567 INFO [stdout] (default task-2) error com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 66 path $.atributos
I did the search of the error and found this:
Gson: Expected begin_array but was STRING
I understand the error but whats the solution?
I can't really control how the JSON builds the arraylist can I?
This is the method prueba in my client:
public String prueba(String nombre, String descripcion, List atributos) {
HashMap map = new HashMap<>();
map.put("nombre", nombre);
map.put("descripcion", descripcion);
map.put("atributos", atributos);
String respuesta = utilidadesRestSeguridad.consumir("prueba", map);
return respuesta;
}
In my client component this is the method that builds the json:
public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
JsonObject json = new JsonObject();
for (Object key : map.keySet()) {
json.addProperty(key.toString(), map.get(key).toString());
}
return json;
}
And thats it guys if you wanna see more code or me to explain something tell me I appreciate any help.
I think maybe the error is in the method generateJSON because of the .toString(), but then how I should handle that case?
Assuming that the line utilidadesRestSeguridad.consumir("prueba", map) ends up calling your generateJSON method downstream, then your issue is likely in the generateJSON() method as you suspect. Basically, you are just adding all elements as strings. If one of the elements in your map is an instance of a List, then you need to call JsonObject#add("atributos", value). For example, you will need something like the following code:
if (map.get(key) instanceof List) {
json.add(key.toString(), map.get(key);
} else {
json.addProperty(key.toString(), map.get(key).toString());
}
As I suspected, the error was in the generateJSON method, needed to add this validation that entpnerd suggested:
public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
JsonObject json = new JsonObject();
for (Object key : map.keySet()) {
if (map.get(key) instanceof List) {
JsonParser parser = new JsonParser();
parser.parse((map.get(key).toString()));
json.add(key.toString(), parser.parse((map.get(key).toString())));
} else {
json.addProperty(key.toString(), map.get(key).toString());
}
}
return json;
}
Notice that I had to use JsonParser, not sure how is it working but at the end that made it work.
Source: How to parse this JSON String with GSON?
Anyways Im gonna try the solution entpnerd is suggesting and post it too.
Here is the implementation of entpnerd suggestion:
public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
JsonObject json = new JsonObject();
for (Object key : map.keySet()) {
if (map.get(key) instanceof List) {
JsonArray jsonArray = new JsonArray();
for (Object object : (ArrayList<Object>) map.get(key)) {
jsonArray.add(object.toString());
}
json.add(key.toString(), jsonArray);
} else {
json.addProperty(key.toString(), map.get(key).toString());
}
}
return json;
}
it works too, you guys decide which one to use, thanks very much.
My only question is, what if the element that is an array, has more arrays inside it, what would you do?
You don't need to get that json value manually, add requestbody annotation to your method parameter
public PruebaJSON prueba(#RequestBody PruebaJSON json){
System.out.println(json);
};

Change my writeJsonFile function from GSON to Jackson

I have some code that takes in a list of descriptors and writes them to different JSON files using the GSON library. I am now trying to change that library to Jackson. I am not a Jackson expert so I'm looking for some help. Here is my code when I am using GSON:
Descriptor Class:
public class Descriptor {
#SerializedName("BatchName")
private String batchName;
#SerializedName("Metadata")
private Metadata metadata;
#SerializedName("SampleInfo")
private SampleInfoJsonModel sampleInfo;
#SerializedName("Files")
private List<String> files;
#SerializedName("ClientData")
private ClientData clientData;
#SerializedName("CaseName")
private String caseName;
public Descriptor() {
this.metadata = new Metadata();
this.sampleInfo = new SampleInfoJsonModel();
this.files = new ArrayList<String>();
this.clientData = new ClientData();
}
public String getBatchName() {
return batchName;
}
public void setBatchName(String batchName) {
this.batchName = batchName;
}
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
public SampleInfoJsonModel getSampleInfo() {
return sampleInfo;
}
public void setSampleInfo(SampleInfoJsonModel sampleInfo) {
this.sampleInfo = sampleInfo;
}
public List<String> getFiles() {
return files;
}
public void setFiles(List<String> files) {
this.files = files;
}
public ClientData getClientData() {
return clientData;
}
public void setClientData(ClientData clientData) {
this.clientData = clientData;
}
public String getCaseName() {
return caseName;
}
public void setCaseName(String caseName) {
this.caseName = caseName;
}
public ClientData getClientDataNoCountryCodes() {
// TODO Auto-generated method stub
return null ;
}
}
My write JSON File function:
public static void writeJsonFile(List<Descriptor> descriptors) {
try {
for(Descriptor descriptor : descriptors) {
BufferedWriter buffWrite = new BufferedWriter(new FileWriter("descriptor_"+descriptor.getCaseName()+".json"));
Gson gson = new GsonBuilder().setPrettyPrinting().create();
buffWrite.write(gson.toJson(descriptor));
buffWrite.close();
}
}
catch (IOException ioe) {
System.err.println("Error while writing to json file in writeJsonFile: ");
ioe.printStackTrace();
}
}
Here is what I have written in Jackson:
BufferedWriter buffWrite = new BufferedWriter(new FileWriter("descriptor_"+descriptor.getCaseName()+".json"));
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
buffWrite.write(mapper.writeValueAsString(descriptor));
Is this the equivalent of the code below in GSON?
BufferedWriter buffWrite = new BufferedWriter(new FileWriter("descriptor_"+descriptor.getCaseName()+".json"));
Gson gson = new GsonBuilder().setPrettyPrinting().create();
buffWrite.write(gson.toJson(descriptor));
buffWrite.close();
I think you are looking for generating a pretty JSON output for your Object and trying to write it into a file.
You have to make sure that you are using #SerializedName equivalent annotation from jackson which is #JsonProperty on your object properties.
Also you can use following to prettify JSON using jackson ObjectMapper
mapper.writerWithDefaultPrettyPrinter().writeValueAsString( descriptorObj )
NOTE that setting SerializationFeature.INDENT_OUTPUT will also help doing the same as you are already thinking.
Also Files APIs are really useful for file related operations.
I hope this will help!

jackson prints empty JSON string

I have a java program which should serialize objects using jackson (play framework). It was working, but I messed it up somehow and now I can't get it working. Here is my serializer
public String serializeObject(Object object) {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = null;
try {
json = ow.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return json;
}
and here is the code which runs it:
return badRequest(serializeObject(new Error("bad input")));
and the error class:
public class Error {
private String error;
public Error(String error) {
this.error = error;
}
}
and as the output, all I get is { }
What is wrong?
Your Error Class's properties need to have setter and getters which you want to show in JSON output
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}

Gson deserializer support multiple types

I want to deserialize a JSON response but I'm not sure about the format. The format can vary in each case. For example the response contains a field named "error" which may be false (boolean) or an object that describes the error eg. "error": { "code": xxx , "description":"etc"}
How should I implement a class that covers both cases? Is there any way to do this?
Thanks
I would prefer using a TypeAdapter for your case:
private static class Error {
private boolean hasError;
private int code;
private String description;
}
private static class ErrorTypeAdapter extends TypeAdapter<Error> {
#Override
public Error read(JsonReader jsonReader) throws IOException {
Error response = null;
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String currentJsonName = jsonReader.nextName();
if("error".equals(currentJsonName)) {
response = new Error();
try {
response.hasError = jsonReader.nextBoolean();
} catch (Exception e) {
response.hasError = true;
jsonReader.beginObject();
}
} else if("code".equals(currentJsonName)) {
response.code = jsonReader.nextInt();
} else if ("description".equals(currentJsonName)) {
response.description = jsonReader.nextString();
}
}
if(response.hasError) {
jsonReader.endObject();
}
jsonReader.endObject();
return response;
}
#Override
public void write(JsonWriter jsonWriter, Error response)
throws IOException {
jsonWriter.beginObject();
jsonWriter.name("hasError").value(response.hasError);
jsonWriter.name("code").value(response.code);
jsonWriter.name("description").value(response.description);
jsonWriter.endObject();
}
}
To test it you can use:
String val1 = "{\"error\": {\"code\": 1 , \"description\":\"etc\"}}";
String val2 = "{\"error\": false}";
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Error.class, new ErrorTypeAdapter());
gsonBuilder.setPrettyPrinting();
final Gson gson = gsonBuilder.create();
gson.fromJson(val1, Error.class);
gson.fromJson(val2, Error.class);
You can read more about TypeAdapters here and also some great examples here.

Converting POJO to JsonNode using a JsonView

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);
}

Categories

Resources