Wie kann ich Jackson anweisen, eine Eigenschaft zu ignorieren, für die ich keine Kontrolle über den Quellcode habe?

111

Kurz gesagt, eine meiner Entitäten hat eine GeometryCollection , die eine Ausnahme auslöst, wenn Sie "getBoundary" aufrufen (das Warum dieses Buches ist ein anderes, sagen wir jetzt, es funktioniert so).

Gibt es eine Möglichkeit, Jackson zu sagen, dass er diesen speziellen Getter nicht einbeziehen soll? Ich weiß, dass ich @JacksonIgnore verwenden kann, wenn ich den Code besitze / kontrolliere. Dies ist jedoch nicht der Fall. Jackson erreicht diesen Punkt durch kontinuierliche Serialisierung der übergeordneten Objekte. Ich habe eine Filteroption in der Jackson-Dokumentation gesehen. Ist das eine plausible Lösung?

Vielen Dank!

Einzelgänger
quelle

Antworten:

168

Sie können Jackson Mixins verwenden . Beispielsweise:

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

Mit diesem Mixin

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

Mit diesem:

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

Bearbeiten:

Dank der Kommentare mit Jackson 2.5+ hat sich die API geändert und sollte mit aufgerufen werden objectMapper.addMixIn(Class<?> target, Class<?> mixinSource)

Amir Raminfar
quelle
1
Und wenn die Eigenschaft maschinengeneriert ist und nicht unterstützte Zeichen im Namen hat? Mögen '@'? JVM erlaubt es, der Java-Compiler jedoch nicht. Hat Jackson eine Lösung dafür?
Markieren Sie den
3
Und in Jackson 2.2 ist esobjectMapper.addMixInAnnotations(Class<?> target, Class<?> mixinSource);
CorayThan
Wie ignoriere ich die Angabe des Eigenschaftsnamens anstelle von Getter?
Erran Morad
67

Eine andere Möglichkeit ist, wenn Sie alle unbekannten Eigenschaften ignorieren möchten, den Mapper wie folgt zu konfigurieren:

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
laloumen
quelle
5
Es wäre großartig, wenn wir den objectMapper so konfigurieren könnten, dass nur bestimmte Eigenschaften ignoriert werden. dh die Ausnahme für alle neuen / unbekannten Felder melden, außer sagen wir 'myfield'. So etwas wiemapper.configure(DeserializationFeature.failOnUnknownPropertiesExcep(new String[] {"myField"}));
ms_27
Beachten Sie, dass dies auch auf einem Lesegerät without()wie mapper.reader().without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
Drew Stephens
Ich würde dringend davon abraten, diesen Mechanismus zu verwenden. Die "strenge" Denkweise in Jackson, die dazu führt, dass Fehler in unbekannten / nicht behandelten Feldern auftreten, ist eine seiner Stärken und entspricht der statisch typisierten / zur Kompilierungszeit analysierten Natur von Java. Es ist viel besser, die Behandlung eines bestimmten Satzes ignorierter Felder zu deaktivieren.
Per Lundberg
26

Verwenden der Java-Klasse

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

Annotation verwenden

@JsonIgnoreProperties(ignoreUnknown=true)
Sireesh Yarlagadda
quelle
11

Annotationsbasierter Ansatz ist besser. Manchmal ist jedoch eine manuelle Bedienung erforderlich. Zu diesem Zweck können Sie ohne ObjectWriter- Methode verwenden .

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
quelle
8
Dieser Ansatz funktioniert bei mir nicht, aber beim Mixin. Nach der Serialisierung erhalte ich immer noch die ignorierten Eigenschaften. Warum haben wir Mixins, wenn wir ohne Attribut () haben?
Erran Morad
9

Mix-In-Annotationen funktionieren hier, wie bereits erwähnt, ziemlich gut. Eine andere Möglichkeit, die über die Eigenschaft @JsonIgnore hinausgeht, ist die Verwendung von @JsonIgnoreType, wenn Sie einen Typ haben, der niemals eingeschlossen werden sollte (dh wenn alle Instanzen von GeometryCollection-Eigenschaften ignoriert werden sollten). Sie können es dann entweder direkt hinzufügen (wenn Sie den Typ steuern) oder Mix-In verwenden, wie:

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

Dies kann praktischer sein, wenn Sie viele Klassen haben, die alle einen einzigen 'IgnoredType getContext ()' - Accessor haben (was bei vielen Frameworks der Fall ist).

StaxMan
quelle
5

Ich hatte ein ähnliches Problem, aber es hing mit den bidirektionalen Beziehungen von Hibernate zusammen. Ich wollte eine Seite der Beziehung zeigen und die andere programmgesteuert ignorieren, je nachdem, mit welcher Ansicht ich es zu tun hatte. Wenn du das nicht kannst, bekommst du böse StackOverflowExceptions. Zum Beispiel, wenn ich diese Objekte hätte

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

public class B{
  Long id;
  A parent;
}

Ich würde das parentFeld in B programmgesteuert ignorieren wollen, wenn ich A betrachte, und das childrenFeld in A ignorieren, wenn ich B betrachte.

Ich habe damit angefangen, Mixins zu verwenden, aber das wird sehr schnell schrecklich. Sie haben so viele nutzlose Klassen herumliegen, die nur zum Formatieren von Daten existieren. Am Ende habe ich meinen eigenen Serializer geschrieben, um dies sauberer zu handhaben: https://github.com/monitorjbl/json-view .

Hier können Sie programmgesteuert angeben, welche Felder ignoriert werden sollen:

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

Außerdem können Sie mithilfe von Platzhalter-Matchern ganz einfach sehr vereinfachte Ansichten angeben:

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

In meinem ursprünglichen Fall bestand die Notwendigkeit für einfache Ansichten wie diese darin, das Nötigste über das Elternteil / Kind zu zeigen, aber es wurde auch für unsere rollenbasierte Sicherheit nützlich. Weniger privilegierte Ansichten von Objekten, die benötigt werden, um weniger Informationen über das Objekt zurückzugeben.

All dies kommt vom Serializer, aber ich habe Spring MVC in meiner App verwendet. Um diese Fälle richtig zu behandeln, habe ich eine Integration geschrieben, die Sie in vorhandene Spring-Controller-Klassen einfügen können:

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

Beide sind auf Maven Central verfügbar. Ich hoffe, es hilft jemand anderem da draußen. Dies ist ein besonders hässliches Problem mit Jackson, das für meinen Fall keine gute Lösung hatte.

monitorjbl
quelle
Wofür ist Import Match.match()?
Pasupathi Rajamanickam
2

Wenn Sie bestimmte Eigenschaften für eine Klasse IMMER ausschließen möchten, können Sie die folgende setMixInResolverMethode verwenden:

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