【问题标题】:Gson Serialize field only if not null or not empty仅当不为空或不为空时才对 Gson 序列化字段
【发布时间】:2013-08-31 18:04:06
【问题描述】:

我有需要将 java 对象转换为 json 的要求。

我为此使用 Gson,但我需要转换器仅序列化非 null 或非空值。

例如:

//my java object looks like
class TestObject{
    String test1;
    String test2;
    OtherObject otherObject = new OtherObject();
}

现在我的 Gson 实例将此对象转换为 json 看起来像

Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";

String jsonStr = gson.toJson(obj);
println jsonStr;

在上面的打印中,结果是

{"test1":"test1", "test2":"", "otherObject":{}}

这里我只想得到结果

{"test1":"test1"}

由于test2为空,otherObject为空,我不希望将它们序列化为json数据。

顺便说一句,我正在使用 Groovy/Grails,所以如果有任何插件会很好,如果没有任何自定义 gson 序列化类的建议会很好。

【问题讨论】:

  • 它怎么知道otherObject 是空的?

标签: java json serialization groovy gson


【解决方案1】:

在我看来,问题不在于 gson。 Gson 正确地跟踪 null 和空字符串之间的差异。您确定要消除这种区别吗?你确定所有使用 TestObject 的类都不关心吗?

如果您不关心差异,您可以做的是在序列化之前将 TestObject 中的空字符串更改为 null。或者更好的是,让 TestObject 中的设置器将空字符串设置为 null;这样,您就可以在类中严格定义空字符串与 null 相同。您必须确保不能在设置器之外设置值。

【讨论】:

  • 好点,但您通常不想同时使用""null。更明智的解决方案可能是避免使用null 并仅使用""。其他库使用空字符串可能比使用空值更好。 数据库可以强制执行 NOT NULL,但不能强制执行“非空”。 +++ 归一化为 null 对我来说不是一个选项,因为我的对象此时仍附加到 Hibernate 会话。
【解决方案2】:

创建您自己的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
    }
}

然后您可以使用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));

产生

 {"test1":"test1"}

GsonBuilder 类有很多方法可以创建您自己的序列化/反序列化策略、注册类型适配器和设置其他参数。

Strings 是一个番石榴类。如果您不想要这种依赖关系,您可以自己检查。

【讨论】:

  • 如果我的 TestObject 类上有其他不同的对象怎么办。例如:通常我的班级会有多个不同类型的嵌入对象
  • @zdesam 你在这个类中进行嵌套序列化。这个类完全依赖于你的TestObject 类,所以它最适合做所有的序列化/反序列化。没有其他组件可以检查您想要的自定义条件。
  • 由于在每个类中整齐地省略空字符串,您的解决方案归结为手动完成所有操作。甚至序列化所有不需要特殊处理的字段。 Gson 所做的已经不多了。
  • 因为 if elses 而被否决 - 使用反射是一种更好的方法,忽略对 T 的需求
  • @Clocker 那么你必须考虑到并不是所有的 POJO 类型在它们的空性定义上都是相同的。为每个(或其中的一组)创建一个适配器可以让你变得明确,我认为这比尝试用反射一般地编写所有这些规则更有用。
【解决方案3】:

我个人不喜欢在TypeAdapter 中使用答案的事实是,您需要描述整个班级的每个字段,可以说是 50 个字段(这意味着 TypeAdapter 中有 50 个 if 块)。
我的解决方案是基于Reflection 和一个事实Gson 默认情况下不会序列化空值字段。
我有一个特殊的类,它为 API 保存数据以创建名为 DocumentModel 的文档,它有大约 50 个字段,我不喜欢将带有“”(空但非空)值或空数组的 String 字段发送到服务器。所以我创建了一个特殊的方法,它返回我的对象​​的副本,其中所有空字段都为空。注意 - 默认情况下,我 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;
}

使用此方法,我不在乎在编写此方法之后是否添加或删除了某些字段。剩下的唯一问题是检查自定义类型字段,它们不是String 或数组,但这取决于特定的类,应该在 if/else 块中额外编码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    相关资源
    最近更新 更多