简体   繁体   English

Gson 仅当不是 null 或不为空时序列化字段

[英]Gson Serialize field only if not null or not empty

I have requirement where I need to convert java object to json.我有需要将 java object 转换为 json 的要求。

I am using Gson for that but i need the converter to only serialize the non null or not empty values.我为此使用 Gson,但我需要转换器仅序列化非 null 或非空值。

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 实例将这个 object 转换为 json 看起来像

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.由于test2为空,otherObject为空,我不希望它们被序列化为json数据。

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.顺便说一句,我正在使用 Groovy/Grails,所以如果有任何插件会很好,如果没有任何建议自定义 gson 序列化 class 会很好。

Create your own TypeAdapter创建你自己的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 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. GsonBuilder类有一堆方法来创建您自己的序列化/反序列化策略、注册类型适配器和设置其他参数。

Strings is a Guava class. Strings是番石榴类。 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 ).我个人不喜欢在TypeAdapter使用 answer 的事实是,您需要描述整个类的每个字段,这些字段可能有 50 个字段(这意味着TypeAdapter 50 个if块)。
My solution is based on Reflection and a fact Gson will not serialize null values fields by default.我的解决方案是基于Reflection和事实Gson默认不会序列化空值字段。
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.我有一个特殊的类,它保存 API 的数据以创建名为 DocumentModel 的文档,它有大约 50 个字段,我不喜欢将带有“”(空但不为空)值或空数组的String字段发送到服务器。 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.注意 - 默认情况下,我的 DocumentModel 实例中的所有数组都初始化为空(零长度)数组,因此它们永远不会为空,您可能应该在检查数组长度之前检查数组是否为空。

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.剩下的唯一问题是检查自定义类型字段,它们不是String或数组,但这取决于特定的类,应该在 if/else 块中额外编码。

It seems to me the problem is not with gson.在我看来,问题不在于 gson。 Gson correctly keeps track of the difference between null and an empty string. Gson 正确跟踪 null 和空字符串之间的差异。 Are you sure you want to erase that distinction?您确定要消除这种区别吗? Are you sure all classes that use TestObject don't care?您确定所有使用 TestObject 的类都不关心吗?

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.如果您不关心差异,您可以做的是在序列化之前将 TestObject 中的空字符串更改为 null。 Or better, make the setters in TestObject such that an empty string is set to null;或者更好的是,使 TestObject 中的 setter 将空字符串设置为 null; that way you define rigidly within the class that an empty string is the same as null.这样您就可以在类中严格定义空字符串与 null 相同。 You'll have to make sure the values cannot be set outside the setters.您必须确保不能在 setter 之外设置这些值。

I have ran into the same problem and found 2 distinct solutions我遇到了同样的问题并找到了 2 个不同的解决方案


  1. Write a custom TypeAdapter for each field class为每个字段编写自定义 TypeAdapter class

TypeAdapter example for String class:字符串 class 的 TypeAdapter 示例:

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

  1. Process the object manually before generating the JSON string在生成 JSON 字符串之前手动处理 object

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM