[ACCEPTED]-Remove empty collections from a JSON with Gson-gson
Steps to follow:
- Convert the JSON String into
Map<String,Object>
using Gson#fromJson() - Iterate the map and remove the entry from the map which are
null
or emptyArrayList
orMap
. - Form the JSON String back from the final map using Gson#toJson().
Note : Use GsonBuilder#setPrettyPrinting() that configures Gson to output Json 2 that fits in a page for pretty printing.
Sample 1 code:
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
...
Type type = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> data = new Gson().fromJson(jsonString, type);
for (Iterator<Map.Entry<String, Object>> it = data.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Object> entry = it.next();
if (entry.getValue() == null) {
it.remove();
} else if (entry.getValue().getClass().equals(ArrayList.class)) {
if (((ArrayList<?>) entry.getValue()).size() == 0) {
it.remove();
}
} else if (entry.getValue() instanceof Map){ //removes empty json objects {}
Map<?, ?> m = (Map<?, ?>)entry.getValue();
if(m.isEmpty()) {
it.remove();
}
}
}
String json = new GsonBuilder().setPrettyPrinting().create().toJson(data);
System.out.println(json);
output;
{
"idPeriodo": 121.0,
"codigo": "2014II",
"activo": false,
"tipoPeriodo": 1.0,
"fechaInicioPreMatricula": "may 1, 2014",
"fechaFinPreMatricula": "jul 1, 2014",
"fechaInicioMatricula": "jul 15, 2014",
"fechaFinMatricula": "ago 3, 2014",
"fechaInicioClase": "ago 9, 2014",
"fechaFinClase": "dic 14, 2014",
"fechaActa": "ene 15, 2015",
"fechaUltModificacion": "May 28, 2014 12:28:26 PM",
"usuarioModificacion": 1.0
}
I tried a solution of @Braj in Kotlin. The 8 idea is to convert JSON to Map, remove nulls 7 and empty arrays, then convert Map back 6 to JSON string.
But it has several disadvantages.
- It can only work with simple POJOs without nestings (no inner classes, lists of classes).
- It converts numbers to doubles (because
Object
is not recognized asint
). - It loses time to convert from String to String.
Alternatively 5 you can try to use Moshi
instead of Gson
, see Broken server response handling with Moshi.
After 4 couple of days I overcame a 1st problem 3 for complex JSONs.
import android.support.annotation.NonNull;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
public class GsonConverter {
private Type type;
private Gson gson;
public GsonConverter() {
type = new TypeToken<Map<String, Object>>() {
}.getType();
gson = new Gson();
}
/**
* Remove empty arrays from JSON.
*/
public String cleanJson(String jsonString) {
Map<String, Object> data = gson.fromJson(jsonString, type);
if (data == null)
return "";
Iterator<Map.Entry<String, Object>> it = data.entrySet().iterator();
traverse(it);
return gson.toJson(data);
}
private void traverse(@NonNull Iterator<Map.Entry<String, Object>> iterator) {
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
Object value = entry.getValue();
if (value == null) {
iterator.remove();
continue;
}
Class<?> aClass = value.getClass();
if (aClass.equals(ArrayList.class)) {
if (((ArrayList) value).isEmpty()) {
iterator.remove();
continue;
}
}
// Recoursively pass all tags for the next level.
if (aClass.equals(ArrayList.class)) {
Object firstItem = ((ArrayList) value).get(0);
Class<?> firstItemClass = firstItem.getClass();
// Check that we have an array of non-strings (maps).
if (firstItemClass.equals(Map.class)) {
// Array of keys and values.
@SuppressWarnings("unchecked")
ArrayList<Map<String, Object>> items = (ArrayList<Map<String, Object>>) value;
for (Map<String, Object> item : items) {
traverse(item.entrySet().iterator());
}
} else if (firstItemClass.equals(LinkedTreeMap.class)) {
// Array of complex objects.
@SuppressWarnings("unchecked")
ArrayList<LinkedTreeMap<String, Object>> items = (ArrayList<LinkedTreeMap<String, Object>>) value;
for (LinkedTreeMap<String, Object> item : items) {
traverse(item.entrySet().iterator());
}
}
} else if (aClass.equals(LinkedTreeMap.class)) {
@SuppressWarnings("unchecked")
LinkedTreeMap<String, Object> value2 = (LinkedTreeMap<String, Object>) value;
traverse(value2.entrySet().iterator());
}
}
}
}
Usage:
YourJsonObject yourJsonObject = new Gson().fromJson(new GsonConverter().cleanJson(json), YourJsonObject.class);
For those who want 2 to use @Braj solution, here is a code in 1 Kotlin.
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
class GsonConverter {
private val type: Type = object : TypeToken<Map<String, Any?>>() {}.type
private val gson = Gson()
private val gsonBuilder: GsonBuilder = GsonBuilder()//.setLongSerializationPolicy(LongSerializationPolicy.STRING)
fun convert(jsonString: String): String {
val data: Map<String, Any?> = gson.fromJson(jsonString, type)
val obj = data.filter { it.value != null && ((it.value as? ArrayList<*>)?.size != 0) }
val json = gsonBuilder/*.setPrettyPrinting()*/.create().toJson(obj)
println(json)
return json
}
}
I have code that can process array or object 5 with different structure and will remove 4 "empty collections or null values" recursively. It 3 works with String not with Gson directly. If 2 it's not critical, it can help you.
Your 1 code will be:
Aiperiodo periodo = periodoService();
//periodo comes from a service method with a lot of values
Gson gson = new Gson();
String json = gson.toJson(periodo);
json = removeNullAndEmptyElementsFromJson(json);
...
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import java.util.Iterator;
import java.util.Map;
public class IoJ {
public static void main(String[] args) {
String j = "{\"query\":\"\",\"name\":null,\"result\":{\"searchResult\":[{\"id\":null,\"phone\":\"123456\",\"familyAdditionalDetails\":[],\"probability\":0.0,\"lastUpdated\":\"2019-05-18T12:03:34Z\",\"empty\":false,\"gender\":\"F\"}]},\"time\":1558181014060}";
// {"query":"","name":null,"result":{"searchResult":[{"id":null,"phone":"123456","familyAdditionalDetails":[],"probability":0.0,"lastUpdated":"2019-05-18T12:03:34Z","empty":false,"gender":"F"}]},"time":1558181014060}
System.out.println(j);
// (additional spaces for easier check)
// {"query":"", "result":{"searchResult":[{ "phone":"123456", "probability":0.0,"lastUpdated":"2019-05-18T12:03:34Z","empty":false,"gender":"F"}]},"time":1558181014060}
System.out.println(removeNullAndEmptyElementsFromJson(j));
}
public static String removeNullAndEmptyElementsFromJson(String jsonString) {
if (jsonString == null) {
return jsonString;
}
try {
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(jsonString);
cleanByTree(element);
jsonString = new GsonBuilder().disableHtmlEscaping().create().toJson(element);
return jsonString;
} catch (Exception e) {
return jsonString;
}
}
private static void cleanByTree(JsonElement e1) {
if (e1 == null || e1.isJsonNull()) {
} else if (e1.isJsonArray()) {
for (Iterator<JsonElement> it = e1.getAsJsonArray().iterator(); it.hasNext(); ) {
JsonElement e2 = it.next();
if (e2 == null || e2.isJsonNull()) {
//it.remove();
} else if (e2.isJsonArray()) {
if (e2.getAsJsonArray().size() == 0) {
it.remove();
} else {
cleanByTree(e2);
}
} else if (e2.isJsonObject()) {
cleanByTree(e2);
}
}
} else {
for (Iterator<Map.Entry<String, JsonElement>> it = e1.getAsJsonObject().entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, JsonElement> eIt = it.next();
JsonElement e2 = eIt.getValue();
if (e2 == null || e2.isJsonNull()) {
//it.remove();
} else if (e2.isJsonArray()) {
if (e2.getAsJsonArray().size() == 0) {
it.remove();
} else {
cleanByTree(e2);
}
} else if (e2.isJsonObject()) {
cleanByTree(e2);
}
}
}
}
}
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.