¿Cómo puedo decirle a jackson que ignore una propiedad para la que no tengo control sobre el código fuente?

112

En pocas palabras, una de mis entidades tiene un GeometryCollection que lanza una excepción cuando llamas a "getBoundary" (el por qué de este es otro libro, por ahora digamos que así funciona).

¿Hay alguna manera de decirle a Jackson que no incluya a ese captador específico? Sé que puedo usar @JacksonIgnore cuando poseo / controlo el código. Pero este no es el caso, jackson termina llegando a este punto mediante la serialización continua de los objetos padres. Vi una opción de filtrado en la documentación de Jackson. ¿Es esa una solución plausible?

¡Gracias!

disidente
fuente

Respuestas:

168

Puede utilizar Jackson Mixins . Por ejemplo:

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

Con este Mixin

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

Con este:

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

Editar:

Gracias a los comentarios, con Jackson 2.5+, la API ha cambiado y debería llamarse con objectMapper.addMixIn(Class<?> target, Class<?> mixinSource)

Amir Raminfar
fuente
1
¿Y si la propiedad se genera por máquina y tiene caracteres no admitidos en su nombre? Me gusta '@'? JVM lo permite, pero el compilador de Java no. ¿Jackson tiene una solución para esto?
marca el
3
Y en Jackson 2.2 esobjectMapper.addMixInAnnotations(Class<?> target, Class<?> mixinSource);
CorayThan
¿Cómo ignoro especificando el nombre de la propiedad en lugar de getter?
Erran Morad
68

Otra posibilidad es que, si desea ignorar todas las propiedades desconocidas, puede configurar el asignador de la siguiente manera:

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
laloumen
fuente
5
Sería genial si pudiéramos configurar el objectMapper para ignorar solo propiedades específicas. es decir, informe de la excepción para todos los campos nuevos / desconocidos, excepto que digamos 'myfield'. Algo comomapper.configure(DeserializationFeature.failOnUnknownPropertiesExcep(new String[] {"myField"}));
ms_27
Tenga en cuenta que esto también se puede configurar en un lector utilizando without()como en:mapper.reader().without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
Drew Stephens
Recomiendo encarecidamente no utilizar este mecanismo. La mentalidad "estricta" de Jackson, que provoca que se generen errores en campos desconocidos / no controlados, es una de sus fortalezas y coincide con la naturaleza analizada en tiempo de compilación / tipo estático de Java. En su lugar, es mucho mejor optar por no manejar un conjunto determinado de campos ignorados.
Per Lundberg
26

Usando la clase Java

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

Usar anotación

@JsonIgnoreProperties(ignoreUnknown=true)
Sireesh Yarlagadda
fuente
11

El enfoque basado en anotaciones es mejor. Pero a veces se necesita una operación manual. Para ello, puede utilizar sin método de ObjectWriter .

ObjectMapper mapper   = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
ObjectWriter writer   = mapper.writer().withoutAttribute("property1").withoutAttribute("property2");
String       jsonText = writer.writeValueAsString(sourceObject);
Fırat KÜÇÜK
fuente
9
Este enfoque no me funciona, pero el mixin sí. Todavía obtengo las propiedades ignoradas después de la serialización. ¿Por qué tenemos mixins cuando tenemos withoutAttribute ()?
Erran Morad
9

Las anotaciones mixtas funcionan bastante bien aquí, como ya se mencionó. Otra posibilidad más allá de @JsonIgnore por propiedad es usar @JsonIgnoreType si tiene un tipo que nunca debe incluirse (es decir, si deben ignorarse todas las instancias de las propiedades de GeometryCollection). Luego puede agregarlo directamente (si controla el tipo), o usando una mezcla, como:

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

Esto puede ser más conveniente si tiene muchas clases que tienen un solo acceso 'IgnoredType getContext ()' o algo así (que es el caso de muchos marcos)

StaxMan
fuente
5

Tuve un problema similar, pero estaba relacionado con las relaciones bidireccionales de Hibernate. Quería mostrar un lado de la relación e ignorar programáticamente el otro, dependiendo de la vista con la que estuviera tratando. Si no puede hacer eso, terminará con desagradables StackOverflowExceptions. Por ejemplo, si tuviera estos objetos

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

public class B{
  Long id;
  A parent;
}

Me gustaría ignorar programáticamente el parentcampo en B si estuviera mirando A, e ignorar el childrencampo en A si estuviera mirando B.

Empecé a usar mixins para hacer esto, pero muy rápidamente se vuelve horrible; tienes tantas clases inútiles por ahí que existen únicamente para formatear datos. Terminé escribiendo mi propio serializador para manejar esto de una manera más limpia: https://github.com/monitorjbl/json-view .

Le permite especificar mediante programación qué campos ignorar:

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")));

También le permite especificar fácilmente vistas muy simplificadas a través de comparadores de comodines:

String json = mapper.writeValueAsString(JsonView.with(list)
    .onClass(A.class, match()
        .exclude("*")
         .include("id", "name")));

En mi caso original, la necesidad de vistas simples como esta era mostrar lo mínimo sobre el padre / hijo, pero también se volvió útil para nuestra seguridad basada en roles. Se necesitaban vistas menos privilegiadas de los objetos para devolver menos información sobre el objeto.

Todo esto proviene del serializador, pero estaba usando Spring MVC en mi aplicación. Para que maneje correctamente estos casos, escribí una integración que puede incluir en las clases de controladores Spring existentes:

@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();
  }
}

Ambos están disponibles en Maven Central. Espero que ayude a alguien más, este es un problema particularmente feo con Jackson que no tuvo una buena solución para mi caso.

monitorjbl
fuente
¿Para qué sirve la importación Match.match()?
Pasupathi Rajamanickam
2

Si desea excluir SIEMPRE ciertas propiedades para cualquier clase, puede usar el setMixInResolvermétodo:

    @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;
        }
    });
Quince
fuente