处理杰克逊序列化的常用方法是什么?

时间:2013-08-29 07:27:50

标签: java json spring hibernate jackson

目前我有一个项目使用Spring-Hibernate和Jackson来处理JSON。我第一次尝试使用Jackson时,我总是得到LazyInitializationException,有时会为多个相互引用的实体进行无限循环。然后我找到了@JsonIgnore@JsonIdentityInfo

现在问题是有时需要忽略属性,但有时我只需要这些属性可序列化。有没有办法有时会忽略几个字段,有时会在运行时序列化字段?

我找到了“Serialization and Deserialization with Jackson: how to programmatically ignore fields?

但是如果我总是必须在注释中使用mix,那么如果一个对象需要检索几十个属性,那将会非常麻烦。例如。在第1页中,我需要propertyApropertyBpropertyC;在第2页中,我需要propertyApropertyC;在第3页我只需要propertyB。仅在这些情况下,我必须为每个页面创建1个类,从而产生3个类。

所以在这种情况下有一种方法来定义类似的东西:

objectA.ignoreAllExcept('propertyA');
String[] properties = {'propertyA', 'propertyC'};
objectB.ignoreAllExcept(properties); // Retrieve propertyA and propertyC
objectC.ignore(properties);

3 个答案:

答案 0 :(得分:1)

您可能正在寻找的是Module。文档说明Modules

  

可以使用ObjectMappers注册的扩展的简单界面,为默认功能提供明确定义的扩展集。

以下是您如何使用它们来实现您想要的效果的示例。注意,还有其他方法可以实现这一点;这只是其中之一。

一个简单的DTO,可用于指定要过滤的属性:

public class PropertyFilter {

    public Class<?> classToFilter;
    public Set<String> propertiesToIgnore = Collections.emptySet();

    public PropertyFilter(Class<?> classToFilter, Set<String> propertiesToIgnore) {
        this.classToFilter = classToFilter;
        this.propertiesToIgnore = propertiesToIgnore;
    }
}

自定义模块,可根据您存储在当前attribute中的某些request过滤掉属性。

public class MyModule extends Module {

    @Override
    public String getModuleName() {
        return "Test Module";
    }

    @Override
    public void setupModule(SetupContext context) {
        context.addBeanSerializerModifier(new MySerializerModifier());
    }

    @Override
    public Version version() {
        // Modify if you need to.
        return Version.unknownVersion();
    }

    public static class MySerializerModifier extends BeanSerializerModifier {

        public BeanSerializerBuilder updateBuilder(SerializationConfig config,
                BeanDescription beanDesc,
                BeanSerializerBuilder builder) {
            List<PropertyFilter> filters =  (List<PropertyFilter>) RequestContextHolder.getRequestAttributes().getAttribute("filters", RequestAttributes.SCOPE_REQUEST);
            PropertyFilter filter = getPropertyFilterForClass(filters, beanDesc.getBeanClass());
            if(filter == null) {
                return builder;
            }

            List<BeanPropertyWriter> propsToWrite = new ArrayList<BeanPropertyWriter>();
            for(BeanPropertyWriter writer : builder.getProperties()) {
                if(!filter.propertiesToIgnore.contains(writer.getName())) {
                    propsToWrite.add(writer);
                }
            }
            builder.setProperties(propsToWrite);
            return builder;
        }


        private PropertyFilter getPropertyFilterForClass(List<PropertyFilter> filters, Class<?> classToCheck) {
            for(PropertyFilter f : filters) {
                if(f.classToFilter.equals(classToCheck)) {
                    return f;
                }
            }
            return null;
        }
    }

}

注意:BeanSerializerModifier类中有一个changeProperties方法更适合更改属性列表(根据文档)。因此,您可以通过适当的更改将updateBuilder中编写的代码移动到changeProperties方法。

现在,您需要使用ObjectMapper注册此自定义module。您可以从应用程序上下文中获取Jackson HTTP消息转换器,并获取其对象映射器。我假设你已经知道如何做到这一点,因为你一直在处理延迟初始化问题。

// Figure out a way to get the ObjectMapper.
MappingJackson2HttpMessageConverter converter = ... // get the jackson-mapper;
converter.getObjectMapper().registerModule(new MyModule())

你完成了。如果要为特定类型的对象自定义序列化,请为其创建PropertyFilter,将其放在List中,并使其可用作当前请求中的属性。这只是一个简单的例子。您可能需要稍微调整一下以满足您的需求。

在您的问题中,您似乎正在寻找一种方法来指定序列化对象本身的属性 - 过滤掉。在我看来,应该避免这种情况,因为过滤掉的属性列表不属于您的实体。但是,如果您确实要这样做,请为属性列表创建一个interface,其中包含settersgetters。假设接口的名称为CustomSerialized然后,您可以修改MyModule类以查找此CustomSerialized接口的实例,并相应地过滤掉属性。

注意:您可能需要根据所使用的库的版本调整/调整一些内容。

答案 1 :(得分:0)

我认为有一种更灵活的方式。您可以以这样的方式配置Jackson,即它将静默忽略延迟加载的属性,而不是停止序列化过程。所以你可以重用同一个类。只需加载所有必要的属性/关系并将其传递给杰克逊。您可以尝试通过声明自定义ObjectMapper并关闭SerializationFeature.FAIL_ON_EMPTY_BEANS功能来执行此操作。希望它有所帮助。

答案 2 :(得分:0)

您可以通过为mixin注释创建静态接口来过滤掉属性而无需修改类。接下来,使用@JsonFilter注释注释该接口。创建SimpleBeanPropertyFilter和SimpleFilterProvider。然后通过调用objectMapper.writer(filterProvider)与过滤器提供程序一起创建一个ObjectWriter