【问题标题】:Jackson: Serialize null object as empty杰克逊:将空对象序列化为空
【发布时间】:2017-08-08 10:27:21
【问题描述】:

我有巨大的 json 文档和映射到该 json 的适当 jaskson 模型。在某些情况下,由于某些数据不存在,因此无法使用所有对象构建所需的 json 文档。

例如我有以下模型:

class First {

    private firstField;
    private secondField;
    etc...
}

class Second {

    private firstField;
    private secondField;
    etc...
}

class General {

    private First first;
    private Second second;
    etc...
}

并且有可能只填充第一个实例:

在通常情况下,它会像这样被序列化:

{
   "first":{
      "firstField":"some_value",
      "secondField":"some_value"
   },
   "second":null
}

但我的目标是序列化 General 类,如下所示:

{  
   "first":{  
      "firstField":"some_value",
      "secondField":"some_value"
   },
   "second":{  
      "firstField":"null",
      "secondField":"null"
   }
}

可以通过在 General 类中进行以下更改来实现这一点,以便默认使用默认构造函数初始化其成员:

class General {

        private First first = new First();
        private Second second = new Second()
        etc...
    }

但是这种方法会导致现有模型发生太多变化,我不确定它是否是最好的一种方法。是否可以创建一些自定义序列化程序来自行执行此操作?

根据https://stackoverflow.com/users/1898563/michael建议编辑:

因此,主要思想是创建能够检查实例是否为空的序列化程序,如果为空,它应该能够使用默认构造函数创建新实例,注意:这个序列化程序不应该基于特定的Second 类,它应该适用于任何将被序列化的对象,除了简单类型。

【问题讨论】:

    标签: java-8 jackson jackson-modules


    【解决方案1】:

    是的,可以创建一个使用反射来执行此操作的自定义序列化程序。您可以通过扩展StdSerializer 来做到这一点。您的实现可能如下所示:

    public class NullSerializer<T> extends StdSerializer<T> {
    
        public NullSerializer() {
            this(null);
        }
    
        public NullSerializer(Class<T> t) {
            super(t);
        }
    
        @Override
        public void serialize(T item, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonProcessingException,
                IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException)
        {
    
            jgen.writeStartObject();
    
            // For all fields of the class you're serializing
            for (final Field field : item.getClass().getDeclaredFields())
            {
                field.setAccessible(true);
    
                Object value = field.get(item);
    
                // if the value is null, create a new instance
                if (value == null)
                {  
                    value = field.getType().getConstructor().newInstance();
                }
    
                // write it
                jgen.writeObject(value);
            }
    
            jgen.writeEndObject();
        }
    }
    

    这依赖于每个字段都有一个公共默认构造函数。您可能想要捕获一些异常,而不是像我所做的那样将它们声明为在签名中抛出。

    您需要向 ObjectMapper 注册此序列化程序。 This article 解释了如何做到这一点。


    我不认为这是一个特别优雅的解决方案,但它应该可以满足您的要求。我会首先避免使用可为空的字段,但在你的情况下这可能是不可能的。

    【讨论】:

    • 感谢您的快速回答,可能我的问题没有以适当的方式描述,实际上主要思想是让序列化程序能够检查除简单类型之外的任何对象是否为空以及是否为空等于 null 它应该能够使用默认构造函数创建新对象,因此它不应该基于某些特定的第二种类型,它应该能够与将被序列化的每个新对象一起使用。
    • 没问题。那么你需要编辑你的问题。
    • 感谢您的提示,但您知道如何在我拥有的所有模型上使用它吗?目前我有用于序列化的全局 ObjectMapper 实例,我将对其进行调整以应用此序列化程序适用于所有模型,但看起来使用这种方法我需要指定将通过 NullSerializer 显式序列化的每种类型
    • ,类似这样的 SimpleModule s = new SimpleModule(); s.addSerializer(Second.class, new NullSerializer());。而且这种方法对我不起作用,因为我在最初的问题中提到了巨大的 json 模型,我需要指定大量模型才能在全球范围内使用这个序列化程序,而且我需要始终保持这个列表是最新的.是否有任何其他方法可以在全球范围内指定它?非常感谢!
    • 你不会做s.addSerializer(General.class ... 吗?无论如何,您可以以类似的方式通过反射循环遍历字段。
    猜你喜欢
    • 2012-03-14
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-22
    相关资源
    最近更新 更多