Jackson - Entscheide, welche Felder serialisiert/deserialisiert werden

Jackson - Entscheide, welche Felder serialisiert / deserialisiert werden

1. Überblick

In diesem Artikel werden wir die verschiedenen Möglichkeiten untersuchen, wie wirif a field is serialized / deserialized by Jackson steuern können oder nicht.

2. Ein öffentliches Feld

Der einfachste Weg, um sicherzustellen, dass ein Feld sowohl serialisierbar als auch deserialisierbar ist, besteht darin, es öffentlich zu machen.

Deklarieren wir eine einfache Klasse mit einer öffentlichen, einer paketprivaten und einer privaten

public class MyDtoAccessLevel {
    private String stringValue;
    int intValue;
    protected float floatValue;
    public boolean booleanValue;
    // NO setters or getters
}

Von den vier Feldern der Klasse werden standardmäßig nur die öffentlichenbooleanValue in JSON serialisiert:

@Test
public void givenDifferentAccessLevels_whenPublic_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("stringValue")));
    assertThat(dtoAsString, not(containsString("intValue")));
    assertThat(dtoAsString, not(containsString("floatValue")));

    assertThat(dtoAsString, containsString("booleanValue"));
}

3. Ein Getter macht ein nicht öffentliches Feld serialisierbar und deserialisierbar

Eine weitere einfache Möglichkeit, ein Feld - insbesondere ein nicht öffentliches Feld - serialisierbar zu machen, ist das Hinzufügen eines Getters:

public class MyDtoWithGetter {
    private String stringValue;
    private int intValue;

    public String getStringValue() {
        return stringValue;
    }
}

Wir erwarten nun, dass das FeldstringValueerialisierbar ist, während das andere private Feld nicht serialisierbar ist, da es keinen Getter hat:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoGetter dtoObject = new MyDtoGetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, not(containsString("intValue")));
}

Unintuitiv macht der Getter auch das private Feld deserialisierbar - denn sobald es einen Getter hat, wird das Feld als Eigenschaft betrachtet.

Schauen wir uns an, wie das funktioniert:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenDeserializable()
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"stringValue\":\"dtoString\"}";
    ObjectMapper mapper = new ObjectMapper();
    MyDtoWithGetter dtoObject = mapper.readValue(jsonAsString, MyDtoWithGetter.class);

    assertThat(dtoObject.getStringValue(), equalTo("dtoString"));
}

4. Ein Setter macht ein nicht öffentliches Feld nur deserialisierbar

Wir haben gesehen, wie der Getter das private Feld serialisierbar und deserialisierbar gemacht hat. Andererseits markiert ein Setter nur das nicht öffentliche Feld als deserialisierbar:

public class MyDtoWithSetter {
    private int intValue;

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

    public int accessIntValue() {
        return intValue;
    }
}

Wie Sie sehen können, hat das privateintValue-Feld diesmal nur einen Setter. Wir haben eine Möglichkeit, auf den Wert zuzugreifen, aber das ist kein Standard-Getter.

Der Unmarshalling-Prozess fürintValue sollte ordnungsgemäß funktionieren:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable()
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"intValue\":1}";
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);

    assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}

Und wie bereits erwähnt, sollte der Setter das Feld nur deserialisierbar, aber nicht serialisierbar machen:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = new MyDtoSetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("intValue")));
}

5. Machen Sie alle Felder global serialisierbar

In einigen Fällen, in denen Sie beispielsweise den Quellcode möglicherweise nicht direkt ändern können, müssen wir die Art und Weise konfigurieren, wie Jackson mit nicht öffentlichen Feldern von außen umgeht.

Diese Art der globalen Konfiguration kann auf ObjectMapper-Ebene durchgeführt werden, indem die FunktionAutoDetectaktiviert wird, um entwederpublic Felder oder Getter / Setter-Methoden für die Serialisierung zu verwenden, oder die Serialisierung für alle Felder aktiviert wird:

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

Der folgende Testfall überprüft, ob alle Mitgliedsfelder (einschließlich nicht öffentlicher) vonMyDtoAccessLevel serialisierbar sind:

@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, containsString("intValue"));
    assertThat(dtoAsString, containsString("booleanValue"));
}

6. Ändern Sie den Namen einer Eigenschaft bei Serialisierung / Deserialisierung

Sie können nicht nur steuern, welches Feld serialisiert oder deserialisiert wird, sondern auchhave control over the way a fields maps to JSON and back. Ich habethis configuration here abgedeckt.

7. Ignorieren Sie ein Feld zur Serialisierung oder Deserialisierung

Nachthis tutorial finden Sie eine Anleitung zum vollständigen Ignorieren eines Felds bei der Serialisierung und Deserialisierung.

Manchmal müssen wir jedoch nur das Feld auf einem ignorieren, aber nicht auf beiden. Jackson ist flexibel genug, um auch diesem interessanten Anwendungsfall gerecht zu werden.

Das folgende Beispiel zeigt einUser-Objekt, das vertrauliche Kennwortinformationen enthält, die nicht in JSON serialisiert werden sollten.

Um dorthin zu gelangen, fügen wir einfach die Annotation@JsonIgnore auf den Getter vonpassword hinzu und aktivieren die Deserialisierung für das Feld, indem wir die Annotation@JsonProperty auf den Setter anwenden:

@JsonIgnore
public String getPassword() {
    return password;
}
@JsonProperty
public void setPassword(String password) {
    this.password = password;
}

Jetzt werden die Kennwortinformationen nicht mehr in JSON serialisiert:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    User userObject = new User();
    userObject.setPassword("thePassword");

    String userAsString = mapper.writeValueAsString(userObject);
    assertThat(userAsString, not(containsString("password")));
    assertThat(userAsString, not(containsString("thePassword")));
}

Der JSON mit dem Kennwort wird jedoch erfolgreich für das ObjektUserdeserialisiert:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect()
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = "{\"password\":\"thePassword\"}";
    ObjectMapper mapper = new ObjectMapper();

    User userObject = mapper.readValue(jsonAsString, User.class);

    assertThat(userObject.getPassword(), equalTo("thePassword"));
}

8. Fazit

In diesem Tutorial wird erläutert, wie Jackson dabeiwhich field is serialized/deserialized and which gets ignored auswählt und wie Sie die vollständige Kontrolle darüber erlangen.

Sie können auch zum nächsten Schritt zum Verständnis von Jackson 2 gelangen, indem Sie mit Artikeln wieignoring a field,deserializing a JSON Array to a Java Array or Collection tiefer tauchen.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie inmy github project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.