gson null when not deserialize
public class Mode {
#Expose(deserialize = false)
public final List<String> list;
public Mode(List<String> list) {
this.list = list;
}
public List<String> getList() {
return list;
}
}
list only serialize not deserialize
public class Entity {
public Mode setting = new Mode(Arrays.asList("1", "2"));
}
add deserialization exclusion strategy:
Gson gson = new GsonBuilder().setPrettyPrinting().addDeserializationExclusionStrategy(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) {
return false;
}
return !annotation.deserialize();
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}).create();
var s = """
{
"setting": {
"list": [
"1",
"2",
"3"
]
}
}
""";
System.out.println(gson.fromJson(s, Entity.class).setting.getList());
The list field of the Mode class is null when deserialize the Entity class
Perfect solution
//Step.1 get json object
JsonObject jsonObject = gson().fromJson("", JsonObject.class);
//Step.2 get all field of the entity
for (Field configField : klass.getDeclaredFields()) {
configField.setAccessible(true);
if (!jsonObject.has(configField.getName())) {
continue;
}
//Step.3 deserialize
Object setting = gson().fromJson(jsonObject.get(configField.getName()).toString(), configField.getType());
//Step.3.1 get all field of the setting
for (Field settingField : setting.getClass().getDeclaredFields()) {
settingField.setAccessible(true);
if (settingField.isAnnotationPresent(Expose.class)) {
if (!settingField.getAnnotation(Expose.class).deserialize()) {
Field declaredField = ReflectUtil.getField(setting.getClass(), settingField.getName());
//Step.3.2 use the value from entity to set the value of setting
declaredField.set(setting, ReflectUtil.getFieldValue(ReflectUtil.getFieldValue(entity, configField.getName()), settingField.getName()));
}
}
}
//Step.4 use the value from step 3 to set the value of entity
configField.set(entity, setting);
}
Related
JSON Response from API :
{
"result":[
{
"ResultType":"SUCCESS"
}
]
}
After Converting to ResultClass.class :
{
"result":[
{
"resultType":null
}
]
}
Expected Output After Converting to ResultClass.class :
{
"result":[
{
"resultType":"SUCCESS"
}
]
}
I am integrating with third party API.I want to change property name while deserializing .I have tried #JsonProperty on filed getter and setter.But the value is not reflected in field resultType.
ResultClass.java
#JsonProperty("result")
List<TestClass> result = new ArrayList<>();
public List<TestClass> getResult() {
return result;
}
public void setResult(List<TestClass> result) {
this.result = result;
}
TestClass.java
#JsonProperty("ResultType")
private String resultType;
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
Note : I have tried JsonObject and it is working fine.I am using HttpClient and HttpResponse for making request.Jackson Version : 2.5.0
2 Solutions are available:
1. Make case-insensitive deserializing
Add this feature on your object mapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
2. Serialize and deserialize with different property names
To change the name of the property ResultType to resultType, you should rather use both #JsonGetter and #JsonSetter:
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonSetter;
public class TestClass {
private String resultType;
#JsonGetter("resultType")
public String getResultType() {
return resultType;
}
#JsonSetter("ResultType")
public void setResultType(String resultType) {
this.resultType = resultType;
}
}
To the best of my understanding, Jackson will
serialize a public instance variable to the variable name
public List<String> myStrings = new ArrayList<>();
serializes to
{ 'myStrings' : [ ... ] }
serialize a private instance variable to the variable name if it has a public getter named getVariable():
private List<String> myStrings = new ArrayList<>();
public List<String> getMyStrings() { return myStrings; }
serializes similar to
{ 'myStrings' : [ ... ] }
However, what I am trying to achieve is to serialize it to a String (instead of array of Strings) based on another method, but keep the JSON key (based on #JsonInclude(JsonInclude.Include.NON_NULL) suppressing the original accessor in some cases
#JsonInclude(JsonInclude.Include.NON_NULL)
private boolean firstStringOnly = true;
private List<String> myStrings = new ArrayList<>();
public List<String> getMyStrings() { return firstStringOnly ? null: myStrings; }
public String getFirstString() { return firstStringOnly ? myStrings.get(0) : null; }
Desired JSON serialization:
For firstStringOnly==true: { 'myStrings' : 'first_String' } (using getFirstString())
For firstStringOnly==false: { 'myStrings' : [ ... ] } (using getMyStrings())
Is this possible to do? I'm specifically looking to avoid using custom serializers, and do this via annotations only.
You can assume a reasonably recent version of Jackson and Java 8.
Just to re-iterate, the question constraints are:
* NO custom serializer
* Both use cases produce the same JSON key
You can generalize getMyStrings() method and make it return Object. And inside check the flag and return first value or all values. Here is my sample
public class tst {
private static class YourObject {
private boolean firstStringOnly;
private List<String> myStrings = new ArrayList<>();
public YourObject(boolean firstStringOnly) {
this.firstStringOnly = firstStringOnly;
this.myStrings.add("str1");
this.myStrings.add("str2");
}
public Object getMyStrings(){
return firstStringOnly ? myStrings.get(0) : myStrings;
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(new YourObject(true)));
System.out.println(mapper.writeValueAsString(new YourObject(false)));
}
}
The output is
{"myStrings":"str1"}
{"myStrings":["str1","str2"]}
EDIT: Sorry, i have misread your initial question. I assume you want to keep both of the typed getters. Would this work for you?
public class TestClass {
private boolean firstStringOnly = true;
private List<String> myStrings = new ArrayList<>();
#JsonIgnore
public boolean isFirstStringOnly() {
return firstStringOnly;
}
public void setFirstStringOnly(boolean firstStringOnly) {
this.firstStringOnly = firstStringOnly;
}
#JsonIgnore
public List<String> getMyStrings() {
return firstStringOnly ? null : myStrings;
}
#JsonIgnore
public String getFirstString() { return firstStringOnly ? myStrings.get(0) : null; }
#JsonProperty("myStrings")
public Object getMyStringsForSerialization() {
return firstStringOnly ? getFirstString() : getMyStrings();
}
public void setMyStrings(List<String> myStrings) {
this.myStrings = myStrings;
}
I have requirement where I need to convert java object to json.
I am using Gson for that but i need the converter to only serialize the non null or not empty values.
For example:
//my java object looks like
class TestObject{
String test1;
String test2;
OtherObject otherObject = new OtherObject();
}
now my Gson instance to convert this object to json looks like
Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
String jsonStr = gson.toJson(obj);
println jsonStr;
In the above print, the result is
{"test1":"test1", "test2":"", "otherObject":{}}
Here i just wanted the result to be
{"test1":"test1"}
Since the test2 is empty and otherObject is empty, i don't want them to be serialized to json data.
Btw, I am using Groovy/Grails so if there is any plugin for this that would be good, if not any suggestion to customize the gson serialization class would be good.
Create your own TypeAdapter
public class MyTypeAdapter extends TypeAdapter<TestObject>() {
#Override
public void write(JsonWriter out, TestObject value) throws IOException {
out.beginObject();
if (!Strings.isNullOrEmpty(value.test1)) {
out.name("test1");
out.value(value.test1);
}
if (!Strings.isNullOrEmpty(value.test2)) {
out.name("test2");
out.value(value.test1);
}
/* similar check for otherObject */
out.endObject();
}
#Override
public TestObject read(JsonReader in) throws IOException {
// do something similar, but the other way around
}
}
You can then register it with Gson.
Gson gson = new GsonBuilder().registerTypeAdapter(TestObject.class, new MyTypeAdapter()).create();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
System.out.println(gson.toJson(obj));
produces
{"test1":"test1"}
The GsonBuilder class has a bunch of methods to create your own serialization/deserialization strategies, register type adapters, and set other parameters.
Strings is a Guava class. You can do your own check if you don't want that dependency.
What I personally don't like in TypeAdapter using answer is the fact you need to describe every field of your entire class which could have lets say 50 fields (which means 50 if blocks in TypeAdapter).
My solution is based on Reflection and a fact Gson will not serialize null values fields by default.
I have a special class which holds data for API to create document called DocumentModel, which has about 50 fields and I don't like to send String fields with "" (empty but not null) values or empty arrays to server. So I created a special method which returns me a copy of my object with all empty fields nulled. Note - by default all arrays in my DocumentModel instance are initialized as empty (zero length) arrays and thus they are never null, you should probably check your arrays for null before checking their length.
public DocumentModel getSerializableCopy() {
Field fields[] = new Field[]{};
try {
// returns the array of Field objects representing the public fields
fields = DocumentModel.class.getDeclaredFields();
} catch (Exception e) {
e.printStackTrace();
}
DocumentModel copy = new DocumentModel();
Object value;
for (Field field : fields) {
try {
value = field.get(this);
if (value instanceof String && TextUtils.isEmpty((String) value)) {
field.set(copy, null);
// note: here array is not being checked for null!
else if (value instanceof Object[] && ((Object[]) value).length == 0) {
field.set(copy, null);
} else
field.set(copy, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return copy;
}
Using this method I don't care if some fields was added or removed after this method was written or whatever. The only problem left - is checking custom type fields, which are not String or array, but this depends to particular class and should be extra coded in if/else blocks.
It seems to me the problem is not with gson. Gson correctly keeps track of the difference between null and an empty string. Are you sure you want to erase that distinction? Are you sure all classes that use TestObject don't care?
What you could do if you don't care about the difference is to change the empty strings to null within a TestObject before serializing it. Or better, make the setters in TestObject such that an empty string is set to null; that way you define rigidly within the class that an empty string is the same as null. You'll have to make sure the values cannot be set outside the setters.
I have ran into the same problem and found 2 distinct solutions
Write a custom TypeAdapter for each field class
TypeAdapter example for String class:
#SuppressWarnings("rawtypes")
public class JSONStringAdapter extends TypeAdapter {
#Override
public String read(JsonReader jsonReader) throws IOException {
String value = jsonReader.nextString();
if(value == null || value.trim().length() == 0) {
return null;
} else {
return value;
}
}
#Override
public void write(JsonWriter jsonWriter, Object object) throws IOException {
String value = String.valueOf(object);
if(value == null || value.trim().length() == 0) {
jsonWriter.nullValue();
} else {
jsonWriter.value(value);
}
}
}
Use:
public class Doggo {
#JsonAdapter(JSONStringAdapter.class)
private String name;
public Doggo(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Doggo aDoggo = new Doggo("");
String jsonString = new Gson().toJson(aDoggo);
}
}
Process the object manually before generating the JSON string
Seems to work on anything, haven't tested the performance:
public static boolean removeEmpty(JSONObject source) {
if (null == source || source.length() == 0) {
return true;
}
boolean isJsonObjectEmpty = false;
for (String key : JSONObject.getNames(source)) {
Object value = source.get(key);
boolean isValueEmpty = isValueEmpty(value);
if(isValueEmpty) {
source.remove(key);
}
}
if(source.length() == 0) {
isJsonObjectEmpty = true;
}
return isJsonObjectEmpty;
}
private static boolean isValueEmpty(Object value) {
if (null == value) {
return true;
}
if (value instanceof JSONArray) {
JSONArray arr = (JSONArray) value;
if(arr.length() > 0) {
List<Integer> indextesToRemove = new ArrayList<>();
for(int i = 0; i< arr.length(); i++) {
boolean isValueEmpty = isValueEmpty(arr.get(i));
if(isValueEmpty) {
indextesToRemove.add(i);
};
}
for(Integer index : indextesToRemove) {
arr.remove(index);
}
if(arr.length() == 0) {
return true;
}
} else {
return true;
}
} else if (value instanceof JSONObject) {
return removeEmpty((JSONObject) value);
} else {
if (JSONObject.NULL.equals(value)
|| null == value
|| value.toString().trim().length() == 0)
) {
return true;
}
}
return false;
}
Use:
public class Doggo {
private String name;
public Doggo(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Doggo aDoggo = new Doggo("");
// if you are not using Type Adapters for your fields
JSONObject aJSONObject1 = new JSONObject(aDoggo);
removeEmpty(aJSONObject1);
String jsonString1 = aJSONObject1.toString();
// if you are using Type Adapters for your fields
Gson gsonParser = new Gson();
JSONObject aJSONObject2 = new JSONObject(gsonParser .toJson(aDoggo));
removeEmpty(aJSONObject2);
String jsonString2 = aJSONObject2.toString();
}
}
In our project we parse JSON with Jackson. We set field saved by field channelId. Problem is that channelId field is parsed later than saved. So at the time we want to set field saved field channelId is null. How we can set field dependency in JSON deserialization, so field saved will be set after channelId?
This is part of our JSON data:
"message":{
"data":{
"text":"Some text"
},
"saved_by":[
2715,
1234
],
"some_boolean_field":false,
"channel_id":8162
}
This is our entity class:
#JsonIgnoreProperties(ignoreUnknown = true)
#org.parceler.Parcel(org.parceler.Parcel.Serialization.BEAN)
public class Message {
#JsonProperty("channel_id")
protected long channelId;
protected boolean saved;
#JsonSetter("saved_by")
public void setSavedBy(Set<Long> savedBy) {
saved = savedBy.contains(getUserIdByChannelId(channelId));
}
public long getChannelId() {
return channelId;
}
public void setChannelId(long channelId) {
this.channelId = channelId;
}
public boolean isSaved() {
return saved;
}
public void setSaved(boolean saved) {
this.saved = saved;
}
public void setData(JsonNode data) throws JsonProcessingException {
JsonNode textNode = data.get("text");
text = textNode != null ? textNode.asText() : "";
components = new ArrayList<>();
JsonNode mediaNode = data.get("media");
if (mediaNode != null) {
MessageComponent[] parsedComponents = AppSession.getInstance().getObjectMapper().treeToValue(mediaNode, MessageComponent[].class);
List<MessageComponent> components = Arrays.asList(parsedComponents).subList(0, parsedComponents.length < 4 ? parsedComponents.length : 4);
this.components.addAll(components);
}
mediaCount = components.size();
}
}
Full JSON:
{
"data":{
"serial":66,
"updated_entity":"bookmark",
"bookmark":{
"message":{
"data":{
"text":"hello"
},
"counted_serial":748,
"saved_by":[
26526,
27758
],
"type":"UserMessage",
"is_reviewed":false,
"channel_id":8128,
"id":2841531,
"replied_message_data":null,
"is_blocked":false,
"is_deleted":false,
"updated_at":"2016-11-21T05:59:52.471Z",
"spam_reported_by":[
],
"created_at":"2016-11-19T15:40:17.027Z",
"uuid":"0b6ba58e-f5e1-4ee5-a9da-041dfc2c85cd",
"liked_by":[
],
"user":{
"last_name":"M",
"id":4537,
"first_name":"John",
"is_deleted":false,
"avatar_thumb":"https:\/\/cdn.site.org\/uploads\/99ef4d68-6eaf-4ba6-aafa-74d1cf895d71\/thumb.jpg"
},
"serial":934
},
"id":6931,
"created_at":"2016-11-21T05:59:52.459Z",
"is_deleted":false,
"updated_at":"2016-11-21T05:59:52.459Z"
}
},
"type":"action_performed"
}
It's a bit hackish, but by making the Message class its own deserialization-builder, you get a kind of "ready for bean creation"-event in which you have access to all of the properties.
My suggestion is that you try the following :
#JsonDeserialize(builder = Message.class)
public class Message {
...
#JsonSetter("saved_by")
public void setSavedBy(Set<Long> savedBy) {
// Merely store the value for later use.
this.savedBy = savedBy;
}
...
public Message build() {
// Calculate value of "saved" field.
this.saved = this.savedBy.contains(getUserIdByChannelId(this.channelId));
return this;
}
// Handling the added challenge.
#JsonProperty("data")
public void setData(JsonNode data) throws JsonProcessingException {
...
}
}
The above takes advantage of the default settings of the JsonPOJOBuilder annotation, namely that the default value for buildMethodName is build.
I have a server that produces the following JSON with Jackson.
{
"$id" : 1,
"employees" : [
{
"$id" : 2,
"name" : "John Rambo",
},
2 // Jackson: reference by ID only
]
}
The list of employees contains the same employee twice. Jackson correctly references the object by it's ID the second time.
I want to deserialize this in a client that uses JSON.net, but this won't work because JSON.net expects me to have the reference wrapped in a json object with a $ref property like this:
{
"$id": "1",
"employees" : [
{
"$id": "2",
"name": "John Rambo"
},
{
"$ref": "2" // JSON.net: reference wrapped in JSON object
}
]
}
Is there a way to make JSON.net correctly consume the Jackson syntax either by configuration or by implementing a custom deserializer?
Here's a custom converter that should work:
public class EmployeeConverter : JsonConverter
{
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
List<Employee> employees = null;
if (reader.TokenType == JsonToken.StartArray)
{
JArray arr = serializer.Deserialize<JArray>(reader);
employees = new List<Employee>(arr.Count);
var employeeMap = new Dictionary<int, Employee>();
foreach (var item in arr)
{
if (item.Type == JTokenType.Object)
{
var employee = item.ToObject<Employee>();
employees.Add(employee);
int id = item["$id"].ToObject<int>();
employeeMap.Add(id, employee);
}
else if (item.Type == JTokenType.Integer)
{
Employee employee = null;
int id = item.ToObject<int>();
if (employeeMap.TryGetValue(id, out employee))
{
employees.Add(employee);
}
}
}
}
return employees;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanConvert(Type objectType)
{
return false;
}
}
... and here's how you'd use it:
public class Company
{
public Company()
{
this.Employees = new List<Employee>();
}
[JsonConverter(typeof(EmployeeConverter))]
public List<Employee> Employees { get; set; }
}
Example: https://dotnetfiddle.net/XooyQC
Basically use a custom converter to deserialize the entire array. First, deserialize the array to a JArray, then inspect each element of the JArray to see if it's a reference or a new object.