我该如何告诉jackson忽略一个我无法控制源代码的属性?

163

长话短说,我的一个实体拥有一个GeometryCollection对象,当调用“getBoundary”方法时会抛出异常(为什么会这样是另一本书的问题,现在我们假设它就是这样工作的)。

我是否可以告诉Jackson不要包括那个特定的getter方法?我知道当我拥有或控制代码时,可以使用@JacksonIgnore。但这不是这种情况,Jackson通过父对象的连续序列化最终到达这一点。我在jackson文档中看到了过滤选项,这是一个可行的解决方案吗?

谢谢!


如何在序列化期间动态忽略字段:https://dev59.com/pGAg5IYBdhLWcg3w1uDy#74594018 - Taras Shpek
8个回答

211
您可以使用Jackson Mixins。例如:

您可以使用Jackson Mixin注释

class YourClass {
  public int ignoreThis() { return 0; }    
}

使用这个Mixin
abstract class MixIn {
  @JsonIgnore abstract int ignoreThis(); // we don't need it!  
}

使用这个:

objectMapper.getSerializationConfig().addMixInAnnotations(YourClass.class, MixIn.class);

编辑:

感谢评论,使用Jackson 2.5+版本后,API已经发生了变化,应该使用objectMapper.addMixIn(Class<?> target, Class<?> mixinSource)进行调用。


1
如果属性是机器生成的并且名称中包含不受支持的字符,例如 '@',JVM 可以处理,但 Java 编译器不行。Jackson 有解决方案吗? - mark
5
在Jackson 2.2中,可以使用objectMapper.addMixInAnnotations(Class<?> target, Class<?> mixinSource);方法来添加混合注解。 - CorayThan
如何通过指定属性名称而不是getter来忽略它? - Erran Morad
同样适用于混入类中定义的属性:@JsonIgnore private HttpClient httpClient; - aksh1618
请确保删除 mapper.disable(MapperFeature.USE_ANNOTATIONS); 如果它之前已经配置过,否则这些 Mixin 注解将不会起作用。 - gkns

81

另一种可能性是,如果您想忽略所有未知的属性,您可以按照以下方式配置映射器:

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

7
如果我们能够配置objectMapper仅忽略特定属性,那将是很好的。例如,对于所有新/未知字段都报告异常,除了'myfield'这个字段。类似于mapper.configure(DeserializationFeature.failOnUnknownPropertiesExcep(new String[] {"myField"})); - ms_27
1
请注意,这也可以在读取器上使用without()进行配置,例如:mapper.reader().without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - Drew Stephens
我强烈建议不要使用这种机制。在Jackson中,“严格”思维方式会导致在未知/未处理字段上引发错误,这是它的优点之一,并且很好地匹配了Java的静态类型/编译时分析的特性。最好选择退出处理给定的一组被忽略的字段。 - Per Lundberg

47

使用Java类

new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

使用注释

@JsonIgnoreProperties(ignoreUnknown=true)

26

基于注解的方法更好。但有时需要手动操作。为此,您可以使用ObjectWriterwithout方法。

ObjectMapper mapper   = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
ObjectWriter writer   = mapper.writer().withoutAttribute("property1").withoutAttribute("property2");
String       jsonText = writer.writeValueAsString(sourceObject);

16
这种方法对我不起作用,但混合方法可以。序列化后我仍然得到被忽略的属性。既然有了withoutAttribute(),我们为什么还需要混合方法? - Erran Morad
1
为什么我们需要mixin,而不是使用withoutAttribute()呢?Mixin无法动态设置。https://dev59.com/N2bWa4cB1Zd3GeqPcfYO#11849523,而我们可以使用“withoutAttributes”来实现。这可能是其中一个原因。 - Vipul Jain
1
其次,“withoutAttribute”用于忽略某个特定名称的字段,而不关心类。Mixin可以帮助您更精细地定义需要忽略这些字段的特定类。 - Vipul Jain
1
@ErranMorad 你最后弄清楚为什么 withoutAttribute() 不起作用了吗? - Saurabh Shrivastava
3
ObjectWriter.withoutAttribute方法的作用与答案中所建议的不同。"attribute" 不是指输入数据的属性名称,也从未在此类语境下引用过,例如在这个评论中指出。该方法似乎是专为内部使用而设计的,如这里所示 - omahdi
显示剩余3条评论

11

如果您想要在任何类中始终排除某些属性,可以使用setMixInResolver方法:

    @JsonIgnoreProperties({"id", "index", "version"})
    abstract class MixIn {
    }

    mapper.setMixInResolver(new ClassIntrospector.MixInResolver(){
        @Override
        public Class<?> findMixInClassFor(Class<?> cls) {
            return MixIn.class;  
        }

        @Override
        public ClassIntrospector.MixInResolver copy() {
            return this;
        }
    });

唯一对我有用的东西,谢谢! - MeBigFatGuy

9

正如先前提到的那样,混合注释在这里非常有效。如果您有一种永远不应该包含的类型(即,如果所有GeometryCollection属性的实例都应该被忽略),除了每个属性@JsonIgnore之外,另一个可能性是使用@JsonIgnoreType。然后,您可以直接添加它(如果您控制该类型),或者使用混合注释,例如:

@JsonIgnoreType abstract class MixIn { }
// and then register mix-in, either via SerializationConfig, or by using SimpleModule

如果你有许多类都有一个单独的“IgnoredType getContext()”访问器(这是许多框架的情况),那么这将更加方便。

8

我曾经遇到过类似的问题,但它与Hibernate的双向关系有关。我想展示关系的一侧并在编程时忽略另一侧,具体取决于我正在处理的视图。如果你不能这样做,最终会出现严重的 StackOverflowException异常。例如,如果我有以下这些对象:

public class A{
  Long id;
  String name;
  List<B> children;
}

public class B{
  Long id;
  A parent;
}

如果我正在查看A,则希望在B中程序化地忽略parent字段,如果我正在查看B,则希望在A中程序化地忽略children字段。

一开始我使用了mixin来做这个,但很快就变得很糟糕;你会有很多无用的类存在,仅仅是为了格式化数据。最终我编写了自己的序列化器以更清晰的方式处理它:https://github.com/monitorjbl/json-view

它允许您通过编程指定要忽略的字段:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(JsonView.class, new JsonViewSerializer());
mapper.registerModule(module);

List<A> list = getListOfA();
String json = mapper.writeValueAsString(JsonView.with(list)
    .onClass(B.class, match()
        .exclude("parent")));

它还可以通过通配符匹配器轻松指定非常简化的视图:
String json = mapper.writeValueAsString(JsonView.with(list)
    .onClass(A.class, match()
        .exclude("*")
         .include("id", "name")));

在我的原始案例中,需要这样的简单视图来展示有关父/子级别的最基本信息,但它也对我们的基于角色的安全性有用。对象的低权限视图需要返回更少关于对象的信息。 所有这些都来自序列化器,但我在我的应用程序中使用了Spring MVC。为了使其正确处理这些情况,我编写了一个集成,可以将其插入到现有的Spring控制器类中:
@Controller
public class JsonController {
  private JsonResult json = JsonResult.instance();
  @Autowired
  private TestObjectService service;

  @RequestMapping(method = RequestMethod.GET, value = "/bean")
  @ResponseBody
  public List<TestObject> getTestObject() {
    List<TestObject> list = service.list();

    return json.use(JsonView.with(list)
        .onClass(TestObject.class, Match.match()
            .exclude("int1")
            .include("ignoredDirect")))
        .returnValue();
  }
}

这两个库都可以在Maven Central上找到。希望能对其他人有所帮助,因为这是一个特别棘手的问题,对于我来说,Jackson没有一个好的解决方案。


Match.match()import 是什么? - Pasupathi Rajamanickam

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接