当前位置: 首页 > 知识库问答 >
问题:

杰克逊 JSON,按路径筛选属性

司马同
2023-03-14

我需要在序列化时动态过滤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' }
   ]
}

最后,我将在RestController中创建一个注释(@JsonFilterProperties),用于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!
}

共有2个答案

方德宇
2023-03-14

有一个名为squiggly的Jackson插件可以实现这一点。

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

您可以将它集成到一个由注释驱动的< code>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;
}
扶珂
2023-03-14

嗯,这很棘手,但可行。您可以使用JacksonFilter功能(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;
    }
}

使用我们的自定义类的自定义版本<code>ObjectMapper</code>:

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

}

现在看看MappingJackson2HttpMessageCon的,你会看到它在内部使用了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>

最后一部分是实现一些东西来检测您的注释并执行实际的配置。为此,可以创建一个< code>@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();
}

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

 类似资料:
  • 我如何告诉Jackson忽略JSON名称? 我有以下POJO: 当我有这样的东西: “ABCName”:“foo”,然后杰克逊没有认出它抛出错误。 它期望的是: “abcName”:“foo”。 代码: 输出:{"abcname":"Foo"} 然后我试着用@JsonProperty(“ABCName”)注释ABCName 在我注释并运行代码之后,我得到的是:{“ABCName”:“Foo”,“A

  • 我想使用Spring的RestTemplate plus Jackson来使用Web服务。我已经学习了几本教程,并且已经达到了创建DAO的目的。这是我获取所有域对象的方法: 但我的Web服务不会立即返回Station对象数组,而是以这种方式返回一个更具语义的表达式: 所以我的问题是,我不知道如何“告诉”RestTemplate在“stations”指示符之后立即解析对象列表,而不创建临时对象,这似

  • 我还在学习如何使用Jackson。。。 所以我有一个JSON对象,它的值有时是整数、长字符串或列表 值:整数 值:字符串 价值:列表 所以总的来说。。。 这是我的POJO模型 这是我的映射器代码 问题是,当我执行代码时,出现以下错误: 我很清楚,之所以会发生这种情况,是因为“value”可以包含三种不同类型中的一种,我如何使代码足够灵活以适应这些类型。。。我总是可以在其他方法中检测值是int、Li

  • 有没有办法让Jackson序列化某个流对象(并在之后关闭)?这样地: 使现代化 澄清:我想流式传输内容,而不仅仅是将其序列化到单个String对象。

  • 问题内容: 我有以下json文件: 但是java模型如下: Jackson解析时会引发异常,因为“ externalId”字段没有getter或setter方法。有没有可以用来忽略json字段的装饰器? 问题答案: 您可以使用注释;如果这只是您要跳过的一个值,则类似于: 或忽略任何无法使用的东西: 还有其他方法可以做到这一点,其余的请查看FasterXML Jackson wiki 。

  • 问题内容: 我有一个特定的JSON节点,它对应于导入org.codehaus.jackson.JsonNode,而不是导入org.codehaus.jackson.map.JsonNode。 我想从上述数组的所有JSON节点中删除“ familyName”和“ middleName”。有什么办法可以做到这一点? 问题答案: 我还没有测试过,但是我认为这样可以满足您的需求: 您还可以使用Jackon