I am using #JsonIgnore property to ignore some attributes in pojo, but these fields are not ignore in json response after parsing json using Gson library please help.
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties(ignoreUnknown=true)
public class RideInvite extends RideInviteOld implements Serializable
{
private static final long serialVersionUID = -3729010679180341959L;
private double newFare = -1;
#JsonIgnore
private long prefPickupDropId;
#JsonIgnore
private String pickupLandmark;
#JsonIgnore
private String dropLandmark;
}
using following code to parse
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
jsonText = gson.toJson(msgObject);
Response after parsing
{"newFare":-1.0,"prefPickupDropId":2,"savePickupDropPoints":false,"pickupDropBasedOnTraffic":true,"allowFareChange":true}
here prefPickupDropId and savePickupDropPoints are json ignored but still value attribute is present in json text
I can not use #Expose for fields because my project is build in such away that ignoring fields which are not required json ignore and same pojos are using for preparing http response. This was working fine earlier but recently I am facing this issue
thanks in advance
Approach1:
Instead of using com.fasterxml.jackson.annotation.JsonIgnore you should use
com.google.gson.annotations.Expose to achieve your requirement.
Here is working code snippet -
import java.io.Serializable;
import com.google.gson.annotations.Expose;
public class RideRecord implements Serializable {
#Expose
private double newFare = -1;
private long prefPickupDropId;
private String pickupLandmark;
private String dropLandmark;
public RideRecord(double newFare, long prefPickupDropId, String pickupLandmark, String dropLandmark) {
super();
this.newFare = newFare;
this.prefPickupDropId = prefPickupDropId;
this.pickupLandmark = pickupLandmark;
this.dropLandmark = dropLandmark;
}
}
use the following code to parse:
RideRecord msgObject = new RideRecord(-1.0, 2, "sfo", "powell bart station");
GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
Gson gson = builder.create();
String jsonText = gson.toJson(msgObject);
System.out.println(jsonText);
It will give output:
{"newFare":-1.0}
because only newFare is the field which is exposed.
you can play with the #Expose attribute to meet your requirements.
Approach2:
If you don't want to use #Expose then also you can achieve your
requirement by just creating Gson object as below -
RideRecord msgObject = new RideRecord(-1.0, 2, "sfo", "powell bart station");
GsonBuilder builder = new GsonBuilder();
builder.addSerializationExclusionStrategy(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
return fieldAttributes.getName().equals("prefPickupDropId") || fieldAttributes.getName().equals("pickupLandmark") || fieldAttributes.getName().equals("dropLandmark");
}
#Override
public boolean shouldSkipClass(Class<?> arg0) {
// TODO Auto-generated method stub
return false;
}
});
Gson gson = builder.create();
String jsonText = gson.toJson(msgObject);
System.out.println(jsonText);
In this case also you will get the same output :
{"newFare":-1.0}
You're using Jackson with GSON and those are too separate frameworks.
One of the approaches is to initialize Gson to exclude fields that do not have the expose annotation.
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Then mark every field you need to use with the #Expose annotation:
ex:
class MyClass {
#Expose public String name;
}
Related
I googled and saw many question and answers but none of it are helping me. Here is the issue. I have a Class
public class ResponseData {
public static transient final int SUCCESS = 1;
public static transient final int FAILED = 0;
public String id;
public int status;
public Object data;
// Constructor, Getters and Setters
}
I'm using ResponseData as the common return object for my server and the server has many APIs. In one of the API, it is setting the data parameter to ArrayList. Then converting as json using Gson (2.8.0).
And then sending back to caller. (It's not HTTP)
public class MyServerClass {
private final Gson gson;
public MyServerClass() {
GsonBuilder builder = new GsonBuilder();
gson = builder.serializeNulls().create();
}
public String someAPI() {
ResponseData responseData = new Response("myid", ResponseData.SUCCESS, Arrays.asList("Some string value", ArrayList<MyCustomObject>, "Some other Value"));
String json = gson.toJson(response)
}
}
And the MyCustomClass is a plain POJO class with some set of attributes.
public class MyCustomClass {
private String name;
private String id;
private String createdTime;
//Constructor, Getters & Setters
}
At the receiving side I have below code.
private Gson gson = null;
GsonBuilder builder = new GsonBuilder();
gson = builder.create();
///
ResponseData response = gson.fromJson(eventData, ResponseData.class);
ArrayList list = (ArrayList) respone.getData();
String val = (String) list.get(0);
List rwData = (List) list.get(1);
for(List<List<String>> entry: rwData) { // Exception is thrown Here. How to get it as List?
//Coverting Data
}
Exception is thrown when trying to get the data as List<List<String>>. How to convert the json string properly here? I cannot use the MyCustomClass at my client layer. That's why trying list
Exception occured:com.google.gson.internal.LinkedTreeMap cannot be cast to java.util.List
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to java.util.List
I am facing with a IMHO a strange behaviour of GSON. Let's take the following example:
{
"Name": "emaborsa",
"Surname": null
}
and my POJO is:
public class User {
#SerializedName("Name")
private String name;
#SerializedName("Surname")
private String surname;
// getter and setter
}
I deserialize it using the following code:
Gson g = new Gson();
User user = g.fromJson(json, User.class);
The variable name is set with "emaborsa", the variable surname I expected it were set to null but there is a string "null " instead.
Is it the correct behaviour or am I missing something? I tried to google it but it is hard to find something related to String and null...
This worked fine for me, using your code as the basis:
package gsonexample3;
import com.google.gson.Gson;
import com.google.gson.annotations.*;
public class User {
public static void main(String[] args) {
Gson g = new Gson();
User user = g.fromJson(json, User.class);
}
#SerializedName("Name")
private String name;
#SerializedName("Surname")
private String surname;
private static String json = "{\"Name\": \"emaborsa\", \"Surname\": null}";
}
Looks like you have to specify that you want to serialize nulls.
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
Coming from - https://howtodoinjava.com/gson/serialize-null-values/
SoapObject data=(SoapObject) envelope.bodyIn;
String result = String.valueOf(((SoapObject) envelope.bodyIn).getProperty(0));
JSONObject _jobjec = new JSONObject(result);
UserId = _jobjec.get("UserId").toString();
UserParentId = _jobjec.get("UserParentId").toString();
UserName = _jobjec.get("UserName").toString();
UserPassword = _jobjec.get("UserPassword").toString();
UserMobile = _jobjec.get("UserMobile").toString();
UserEmail = _jobjec.get("UserEmail").toString();
UserMpin = _jobjec.get("UserMpin").toString();
This is my COde i am trying to JOSon Parse and get value but what happen { ,} remove in result when i make json Object and trying and get value then i get Excepion i am getting
String.valueOf(((SoapObject) envelope.bodyIn).getProperty(0) :
{"UserId":"2","UserParentId":"1","UserName":"Anilkumar","UserPassword":"12546",
"UserMobile":"8130513899","UserEmail":"anilaat87#gmail.com","UserMpin":"7890",
"UserBalance":"20.0000","UserResponseMessage":"Is Valid"}
but unable to parse it
Directly using JSONObject to parse POJO is tedious and error prone, recommended using one of the below libraries:
GSON
JACKSON
MOSHI
JSON.simple
First, define your POJO class, you can use some online service, e.g. this one or this,
Just paste your example json string, then you can get below pojo class (you don't need to write it on your own) in 2 seconds:
package com.example;
import javax.annotation.Generated;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Example {
#SerializedName("UserId")
#Expose
public String userId;
#SerializedName("UserParentId")
#Expose
public String userParentId;
#SerializedName("UserName")
#Expose
public String userName;
#SerializedName("UserPassword")
#Expose
public String userPassword;
#SerializedName("UserMobile")
#Expose
public String userMobile;
#SerializedName("UserEmail")
#Expose
public String userEmail;
#SerializedName("UserMpin")
#Expose
public String userMpin;
#SerializedName("UserBalance")
#Expose
public String userBalance;
#SerializedName("UserResponseMessage")
#Expose
public String userResponseMessage;
}
Then to get the java object, use below gson calls:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
Example instance = gson.fromJson(jsonString, Example.class);
If you use Jackson or other libraries, the same process applies, and they differ only in the detailed function calls.
you have to write code like this
SoapObject data=(SoapObject) envelope.bodyIn;
String result = String.valueOf(((SoapObject)envelope.bodyIn).getProperty(0));
JSONObject _jobjec = new JSONObject(result);
UserId = _jobjec.getString("UserId");
UserParentId = _jobjec.getString("UserParentId");
UserName = _jobjec.getString("UserName");
UserPassword = _jobjec.getString("UserPassword");
UserMobile = _jobjec.getString("UserMobile");
UserEmail = _jobjec.getString("UserEmail");
UserMpin = _jobjec.getString("UserMpin");
I've been trying to upgrade the JSON modules to use the FasterXML (2.6.3) versions of Jackson instead of the old Codehaus modules. During the upgrade, I've noticed that the naming strategy differs when using FasterXML instead of Codehaus.
Codehaus was more flexible when it came to the naming strategy. The test below highlights the issue I'm facing with FasterXML. How can I configure the ObjectMapper so it follows the same strategy like Codehaus?
I cannot alter the JSONProperty annotations as there are hundreds of them. I would like the upgrade to be backwards compatible with respect to the naming strategy.
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
/*import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.PropertyNamingStrategy;*/
import org.junit.Assert;
import org.junit.Test;
public class JSONTest extends Assert {
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Product {
#JsonProperty(value = "variationId")
private String variantId;
#JsonProperty(value = "price_text")
private String priceText;
#JsonProperty(value = "listPrice")
public String listPrice;
#JsonProperty(value = "PRODUCT_NAME")
public String name;
#JsonProperty(value = "Product_Desc")
public String description;
}
private static final String VALID_PRODUCT_JSON =
"{ \"list_price\": 289," +
" \"price_text\": \"269.00\"," +
" \"variation_id\": \"EUR\"," +
" \"product_name\": \"Product\"," +
" \"product_desc\": \"Test\"" +
"}";
#Test
public void testDeserialization() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
Product product = mapper.readValue(VALID_PRODUCT_JSON, Product.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(product));
assertNotNull(product.listPrice);
assertNotNull(product.variantId);
assertNotNull(product.priceText);
assertNotNull(product.name);
assertNotNull(product.description);
}
}
#JsonProperty overrides any PropertyNamingStrategy in fasterxml since version 2.4.0. However, yet-to-be-released version 2.7.0 will provide a feature to allow you to opt back in to the old behavior. There is also an unimplemented suggestion to toggle this at the per-annotation level, but that would not really help you.
It appears that Codehaus does apply the PropertyNamingStrategy on top of the #JsonProperty values when mapping, although I can't find any clear docs on that. This appears to have been the behavior in fasterxml before 2.4.0 as well. Here is another example of someone noticing the same difference in behavior.
Although the solution provided by SkinnyJ is perfect for your problem, but if you can't wait till 2.7 is released, you can apply the below hack to get around the problem.
The idea is to transform the incoming JSON to match the attributes in your bean definition. Below code does that. Following points should be noted:
If you are dealing with nested structures, you will have to implement a recursive function to achieve this transformation.
There is a little overhead involved in doing the transformation.
Code:
public class JSONTest extends Assert {
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Product {
#JsonProperty(value = "variationId")
private String variantId;
#JsonProperty(value = "price_text")
private String priceText;
#JsonProperty(value = "listPrice")
public String listPrice;
#JsonProperty(value = "PRODUCT_NAME")
public String name;
#JsonProperty(value = "Product_Desc")
public String description;
}
private static final String VALID_PRODUCT_JSON =
"{ \"list_price\": 289," +
" \"price_text\": \"269.00\"," +
" \"variation_id\": \"EUR\"," +
" \"product_name\": \"Product\"," +
" \"product_desc\": \"Test\"" +
"}";
#Test
public void testDeserialization() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
//Capture the original JSON in org.json.JSONObject
JSONObject obj = new JSONObject(VALID_PRODUCT_JSON);
JSONArray keys = obj.names();
//New json object to be created using property names defined in bean
JSONObject matchingJson = new JSONObject();
//Map of lowercased key to original keys in incoming json. eg: Prod_id > prodid
Map<String, String> jsonMappings = new LinkedHashMap<String, String>();
for (int i = 0; i < keys.length(); i++) {
String key = lowerCaseWithoutUnderScore(keys.getString(i));
String value = keys.getString(i);
jsonMappings.put(key, value);
}
/*
* Iternate all jsonproperty beans and create new json
* such that keys in json map to that defined in bean
*/
Field[] fields = Product.class.getDeclaredFields();
for (Field field : fields) {
JsonProperty prop = field.getAnnotation(JsonProperty.class);
String propNameInBean = prop.value();
String keyToLook = lowerCaseWithoutUnderScore(propNameInBean);
String keyInJson = jsonMappings.get(keyToLook);
matchingJson.put(propNameInBean, obj.get(keyInJson));
}
String json = matchingJson.toString();
System.out.println(json);
//Pass the matching json to Object mapper
Product product = mapper.readValue(json, Product.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(product));
assertNotNull(product.listPrice);
assertNotNull(product.variantId);
assertNotNull(product.priceText);
assertNotNull(product.name);
assertNotNull(product.description);
}
private String lowerCaseWithoutUnderScore(String key){
return key.replaceAll("_", "").toLowerCase();
}
}
I have the following code
Gson gson = new Gson();
String json = gson.toJson(criteria.list()); // list is passed by Hibernate
The result would be something like this:
{creationTime:0, enabled:true, id:1, loginDuration:0, online:false, userName:someone}
I would like to add new attribute (DT_RowId which has the same value as id) within the JSON response. The end result should be like this:
{creationTime:0, enabled:true, id:1, loginDuration:0, online:false, userName:someone, DT_RowId=1}
UPDATED
I created a field with #Transient annotation on the entity in order to solve this issue.
...
#Transient
private long DT_RowId;
public void setId(long id) {
this.id = id;
this.DT_RowId=id;
}
...
However the setId function was never been called. Can someone enlighten me on this ?
GSON won't call your getters and setters. It accesses member vars directly via reflection. To accomplish what you are trying to do, you will need to use a GSON custom serializer/deserializer. The GSON docs on custom serializers/deserializers provide some examples for how to do this.
Here is a working example with a passing JUnit test that demonstrates how to do it:
Entity.java
public class Entity {
protected long creationTime;
protected boolean enabled;
protected long id;
protected long loginDuration;
protected boolean online;
protected String userName;
protected long DT_RowId;
}
EntityJsonSerializer.java
import java.lang.reflect.Type;
import com.google.gson.*;
public class EntityJsonSerializer implements JsonSerializer<Entity> {
#Override
public JsonElement serialize(Entity entity, Type typeOfSrc, JsonSerializationContext context) {
entity.DT_RowId = entity.id;
Gson gson = new Gson();
return gson.toJsonTree(entity);
}
}
JSONTest.java
import static org.junit.Assert.*;
import org.junit.Test;
import com.google.gson.*;
public class JSONTest {
#Test
public final void testSerializeWithDTRowId() {
Entity entity = new Entity();
entity.creationTime = 0;
entity.enabled = true;
entity.id = 1;
entity.loginDuration = 0;
entity.online = false;
entity.userName = "someone";
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Entity.class, new EntityJsonSerializer());
Gson gson = builder.create();
String json = gson.toJson(entity);
String expectedJson = "{\"creationTime\":0,\"enabled\":true,\"id\":1,\"loginDuration\":0,\"online\":false,\"userName\":\"someone\",\"DT_RowId\":1}";
assertEquals(expectedJson, json);
}
}