【问题标题】:Jackson JSON, filtering properties by pathJackson JSON,按路径过滤属性
【发布时间】:2015-08-04 13:59:28
【问题描述】:

我需要在序列化时动态过滤 bean 属性。

@JsonView 不适合我。

假设我的 Bean(作为 Json 表示法):

{
   id: '1',
   name: 'test',
   children: [
      { id: '1.1', childName: 'Name 1.1' },
      { id: '1.2', childName: 'Name 1.2' }
   ]
}

我想编写具有以下属性的 JSON:

// configure the ObjectMapper to only serialize this properties:
[ "name", "children.childName" ]

预期的 JSON 结果是:

{
   name: 'test',
   children: [
      { childName: 'Name 1.1' },
      { childName: 'Name 1.2' }
   ]
}

最后,我将创建一个注释 (@JsonFilterProperties) 以在我的 RestControllers 中与 Spring 一起使用,如下所示:

@JsonFilterProperties({"name", "children.childName"}) // display only this fields
@RequestMapping("/rest/entity")
@ResponseBody
public List<Entity> findAll() {
     return serviceEntity.findAll(); // this will return all fields populated!
}

【问题讨论】:

  • 你不能使用@JsonIgnore
  • 不@SercanOzdemir,我不能,因为这必须由调用者配置,而不是静态配置。每个调用者可以配置不同的路径进行序列化。
  • 好吧,然后使用Mixin
  • 您能否为您的子对象创建一个只包含您需要的属性的超类?
  • 没有@Mena(再看看问题的结尾,我已经改进了细节)

标签: java json spring-mvc jackson


【解决方案1】:

嗯,这很棘手但可行。您可以使用 Jacksons Filter 功能 (http://wiki.fasterxml.com/JacksonFeatureJsonFilter) 进行一些小改动。首先,我们将使用类名作为过滤器 id,这样您就不必将 @JsonFIlter 添加到您使用的每个实体:

public class CustomIntrospector extends JacksonAnnotationIntrospector {

    @Override
    public Object findFilterId(AnnotatedClass ac) {
        return ac.getRawType();
    }
}

下一步,让超类的过滤器适用于它的所有子类:

public class CustomFilterProvider extends SimpleFilterProvider {

    @Override
    public BeanPropertyFilter findFilter(Object filterId) {
        Class id = (Class) filterId;
        BeanPropertyFilter f = null;
        while (id != Object.class && f == null) {
            f = _filtersById.get(id.getName());
            id = id.getSuperclass();
        }
        // Part from superclass
        if (f == null) {
            f = _defaultFilter;
            if (f == null && _cfgFailOnUnknownId) {
                throw new IllegalArgumentException("No filter configured with id '" + filterId + "' (type " + filterId.getClass().getName() + ")");
            }
        }
        return f;
    }
}

使用我们的自定义类的ObjectMapper 的自定义版本:

public class JsonObjectMapper extends ObjectMapper {
    CustomFilterProvider filters;

    public JsonObjectMapper() {
        filters = new CustomFilterProvider();
        filters.setFailOnUnknownId(false);
        this.setFilters(this.filters);
        this.setAnnotationIntrospector(new CustomIntrospector());
    }

    /* You can change methods below as you see fit. */

    public JsonObjectMapper addFilterAllExceptFilter(Class clazz, String... property) {
        filters.addFilter(clazz.getName(), SimpleBeanPropertyFilter.filterOutAllExcept(property));
        return this;
    }

    public JsonObjectMapper addSerializeAllExceptFilter(Class clazz, String... property) {
        filters.addFilter(clazz.getName(), SimpleBeanPropertyFilter.serializeAllExcept(property));
        return this;
    }        

}

现在看看MappingJackson2HttpMessageConverter,你会发现它在内部使用了ObjectMapper 的一个实例,因此如果你想要同时进行不同的配置(针对不同的请求),你就不能使用它。您需要范围为ObjectMapper 的请求和使用它的适当消息转换器:

public abstract class DynamicMappingJacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {

    // Spring will override this method with one that provides request scoped bean
    @Override
    public abstract ObjectMapper getObjectMapper();

    @Override
    public void setObjectMapper(ObjectMapper objectMapper) {
        // We dont need that anymore
    }

    /* Additionally, you need to override all methods that use objectMapper  attribute and change them to use getObjectMapper() method instead */

}

添加一些bean定义:

<bean id="jsonObjectMapper" class="your.package.name.JsonObjectMapper" scope="request">
    <aop:scoped-proxy/>
</bean>

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="your.package.name.DynamicMappingJacksonHttpMessageConverter">
            <lookup-method name="getObjectMapper" bean="jsonObjectMapper"/>              
        </bean>
    </mvc:message-converters>       
</mvc:annotation-driven>

最后一部分是实现一些可以检测您的注释并执行实际配置的东西。为此,您可以创建一个@Aspect。比如:

@Aspect
public class JsonResponseConfigurationAspect {

@Autowired
private JsonObjectMapper objectMapper;

@Around("@annotation(jsonFilterProperties)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    /* Here you will have to determine return type and annotation value from jointPoint object. */
    /* See http://stackoverflow.com/questions/2559255/spring-aop-how-to-get-the-annotations-of-the-adviced-method for more info */
    /* If you want to use things like 'children.childName' you will have to  use reflection to determine 'children' type, and so on. */    
}

}

就个人而言,我以不同的方式使用它。我不使用注释,只是手动进行配置:

@Autowired
private JsonObjectMapper objectMapper;

@RequestMapping("/rest/entity")
@ResponseBody
public List<Entity> findAll() {
    objectMapper.addFilterAllExceptFilter(Entity.class, "name", "children"); 
    objectMapper.addFilterAllExceptFilter(EntityChildren.class, "childName"); 
    return serviceEntity.findAll();
}

附:这种方法有一个主要缺陷:你不能为一个类添加两个不同的过滤器。

【讨论】:

  • 上面的代码有没有可用的github链接?
  • 恐怕不行,除非别人把它放在那里。
【解决方案2】:

有一个名为 squiggly 的 Jackson 插件可以做到这一点。

String filter = "name,children[childName]";
ObjectMapper mapper = Squiggly.init(this.objectMapper, filter);
mapper.writeValue(response.getOutputStream(), myBean);

您可以将它集成到 MessageConverter 或类似的,由注释驱动,您认为合适。


如果您有固定数量的可能选项,那么也有一个静态解决方案:@JsonView

public interface NameAndChildName {}
@JsonView(NameAndChildName.class)
@ResponseBody
public List<Entity> findAll() {
     return serviceEntity.findAll();
}
public class Entity {
    public String id;

    @JsonView(NameAndChildName.class)
    public String name;

    @JsonView({NameAndChildName.class, SomeOtherView.class})
    public List<Child> children;
}
public class Child {
    @JsonView(SomeOtherView.class)
    public String id;

    @JsonView(NameAndChildName.class)
    public String childName;
}

【讨论】:

    猜你喜欢
    • 2012-06-20
    • 1970-01-01
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    相关资源
    最近更新 更多