Serialize Only Felder, die mit Jackson benutzerdefinierten Kriterien entsprechen

Serialisieren Sie nur Felder, die mit Jackson ein benutzerdefiniertes Kriterium erfüllen

1. Überblick

Dieses Tutorial wird veranschaulichen, wie wiruse Jackson to only serialize a field if it meets a specific, custom criteria können.

Angenommen, wir möchten einen ganzzahligen Wert nur dann serialisieren, wenn er positiv ist. Wenn dies nicht der Fall ist, möchten wir ihn vollständig überspringen.

Wenn Sie tiefer graben undother cool things you can do with the Jackson 2 lernen möchten, gehen Sie zuthe main Jackson tutorial.

2. Verwenden Sie Jackson Filter, um den Serialisierungsprozess zu steuern

Zuerst müssen wir den Filter für unsere Entität mithilfe der Annotation@JsonFilterdefinieren:

@JsonFilter("myFilter")
public class MyDto {
    private int intValue;

    public MyDto() {
        super();
    }

    public int getIntValue() {
        return intValue;
    }

    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }
}

Dann müssen wir unsere benutzerdefiniertenPropertyFilter definieren:

PropertyFilter theFilter = new SimpleBeanPropertyFilter() {
   @Override
   public void serializeAsField
    (Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
     throws Exception {
      if (include(writer)) {
         if (!writer.getName().equals("intValue")) {
            writer.serializeAsField(pojo, jgen, provider);
            return;
         }
         int intValue = ((MyDtoWithFilter) pojo).getIntValue();
         if (intValue >= 0) {
            writer.serializeAsField(pojo, jgen, provider);
         }
      } else if (!jgen.canOmitFields()) { // since 2.3
         writer.serializeAsOmittedField(pojo, jgen, provider);
      }
   }
   @Override
   protected boolean include(BeanPropertyWriter writer) {
      return true;
   }
   @Override
   protected boolean include(PropertyWriter writer) {
      return true;
   }
};

Dieser Filter enthält die tatsächliche Logik, die basierend auf ihrem Wert entscheidet, ob das FeldintValueserialized or not sein soll.

Als nächstes verknüpfen wir diesen Filter mitObjectMapper und serialisieren eine Entität:

FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", theFilter);
MyDto dtoObject = new MyDto();
dtoObject.setIntValue(-1);

ObjectMapper mapper = new ObjectMapper();
String dtoAsString = mapper.writer(filters).writeValueAsString(dtoObject);

Und schließlich können wir überprüfen, ob das FeldintValuetatsächlichnot part of the marshalled JSON output ist:

assertThat(dtoAsString, not(containsString("intValue")));

3. Objekte bedingt überspringen

Lassen Sie uns nun diskutieren, wie Objekte beim Serialisieren basierend auf der Eigenschaftvalue übersprungen werden. Wir werden alle Objekte überspringen, bei denen die Eigenschafthiddentrue ist:

3.1. Versteckbare Klassen

Schauen wir uns zunächst dieHidable-Schnittstelle an:

@JsonIgnoreProperties("hidden")
public interface Hidable {
    boolean isHidden();
}

Und wir haben zwei einfache Klassen, die diese SchnittstellePerson,Address implementieren:

Person Klasse:

public class Person implements Hidable {
    private String name;
    private Address address;
    private boolean hidden;
}

UndAddress Klasse:

public class Address implements Hidable {
    private String city;
    private String country;
    private boolean hidden;
}

Hinweis: Wir haben@JsonIgnoreProperties(“hidden”) verwendet, um sicherzustellen, dass die Eigenschafthiddenelbst nicht in JSON enthalten ist

3.2. Benutzerdefinierter Serializer

Weiter - hier ist unser benutzerdefinierter Serializer:

public class HidableSerializer extends JsonSerializer {

    private JsonSerializer defaultSerializer;

    public HidableSerializer(JsonSerializer serializer) {
        defaultSerializer = serializer;
    }

    @Override
    public void serialize(Hidable value, JsonGenerator jgen, SerializerProvider provider)
      throws IOException, JsonProcessingException {
        if (value.isHidden())
            return;
        defaultSerializer.serialize(value, jgen, provider);
    }

    @Override
    public boolean isEmpty(SerializerProvider provider, Hidable value) {
        return (value == null || value.isHidden());
    }
}


Beachten Sie, dass:

  • Wenn das Objekt nicht übersprungen wird, delegieren wir die Serialisierung an den standardmäßigen Serialisierer mit Injection.

  • Wir haben die MethodeisEmpty() überschrieben - um sicherzustellen, dass im Fall eines versteckten Objekts eine Eigenschaft ist, wird der Eigenschaftsname auch von JSON ausgeschlossen.

3.3. Verwenden vonBeanSerializerModifier

Schließlich müssen wirBeanSerializerModifier verwenden, um den Standard-Serializer in unsere benutzerdefiniertenHidableSerializer einzufügen - wie folgt:

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_EMPTY);
mapper.registerModule(new SimpleModule() {
    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.addBeanSerializerModifier(new BeanSerializerModifier() {
            @Override
            public JsonSerializer modifySerializer(
              SerializationConfig config, BeanDescription desc, JsonSerializer serializer) {
                if (Hidable.class.isAssignableFrom(desc.getBeanClass())) {
                    return new HidableSerializer((JsonSerializer) serializer);
                }
                return serializer;
            }
        });
    }
});



3.4. Beispielausgabe

Hier ist ein einfaches Serialisierungsbeispiel:

Address ad1 = new Address("tokyo", "jp", true);
Address ad2 = new Address("london", "uk", false);
Address ad3 = new Address("ny", "usa", false);
Person p1 = new Person("john", ad1, false);
Person p2 = new Person("tom", ad2, true);
Person p3 = new Person("adam", ad3, false);

System.out.println(mapper.writeValueAsString(Arrays.asList(p1, p2, p3)));

Und die Ausgabe ist:

[
    {
        "name":"john"
    },
    {
        "name":"adam",
        "address":{
            "city":"ny",
            "country":"usa"
        }
    }
]

3.5. Test

Zum Schluss - hier ein paar Testfälle:

Erster Fall,nothing is hidden:

@Test
public void whenNotHidden_thenCorrect() throws JsonProcessingException {
    Address ad = new Address("ny", "usa", false);
    Person person = new Person("john", ad, false);
    String result = mapper.writeValueAsString(person);

    assertTrue(result.contains("name"));
    assertTrue(result.contains("john"));
    assertTrue(result.contains("address"));
    assertTrue(result.contains("usa"));
}

Als nächstesonly address is hidden:

@Test
public void whenAddressHidden_thenCorrect() throws JsonProcessingException {
    Address ad = new Address("ny", "usa", true);
    Person person = new Person("john", ad, false);
    String result = mapper.writeValueAsString(person);

    assertTrue(result.contains("name"));
    assertTrue(result.contains("john"));
    assertFalse(result.contains("address"));
    assertFalse(result.contains("usa"));
}

Nunentire person is hidden:

@Test
public void whenAllHidden_thenCorrect() throws JsonProcessingException {
    Address ad = new Address("ny", "usa", false);
    Person person = new Person("john", ad, true);
    String result = mapper.writeValueAsString(person);

    assertTrue(result.length() == 0);
}

4. Fazit

Diese Art der erweiterten Filterung ist unglaublich leistungsfähig und ermöglicht eine sehr flexible Anpassung des JSON bei der Serialisierung komplexer Objekte mit Jackson.

Eine flexiblere, aber auch komplexere Alternative wäre die Verwendung eines vollständig benutzerdefinierten Serializers zur Steuerung der JSON-Ausgabe. Wenn diese Lösung also nicht flexibel genug ist, lohnt es sich möglicherweise, dies zu prüfen.

Die Implementierung all dieser Beispiele und Codefragmentecan be found in my github project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.