So sortieren Sie Enumen als JSON-Objekte mit Jackson

Serialisieren und Deserialisieren von Enums mit Jackson

1. Überblick

Dieses kurze Tutorial zeigt, wie SieJava Enums are serialized and deserialized with Jackson 2 steuern.

Um etwas tiefer zu graben undother cool things we can do Jackson 2 zu lernen, gehen Sie zuthe main Jackson tutorial.

2. Steuern der Enum-Darstellung

Definieren wir die folgende Aufzählung:

public enum Distance {
    KILOMETER("km", 1000),
    MILE("miles", 1609.34),
    METER("meters", 1),
    INCH("inches", 0.0254),
    CENTIMETER("cm", 0.01),
    MILLIMETER("mm", 0.001);

    private String unit;
    private final double meters;

    private Distance(String unit, double meters) {
        this.unit = unit;
        this.meters = meters;
    }

    // standard getters and setters
}

3. Serialisierung von Enums in JSON

3.1. Standard-Enum-Darstellung

Standardmäßig repräsentiert Jackson Java Enums als einfache Zeichenfolge - zum Beispiel:

new ObjectMapper().writeValueAsString(Distance.MILE);

Wird darin enden, dass:

"MILE"

Was wir beim Marshalling dieserEnum to a JSON Object erhalten möchten, ist, etwas zu geben wie:

{"unit":"miles","meters":1609.34}

3.2. Aufzählung als JSON-Objekt

Ab Jackson 2.1.2 gibt es jetzt eine Konfigurationsoption, die diese Art der Darstellung verarbeiten kann. Dies kann über die Annotation@JsonFormatauf Klassenebene erfolgen:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Distance { ... }

Dies führt zum gewünschten Ergebnis, wenn dieseenum fürDistance.MILE serialisiert werden:

{"unit":"miles","meters":1609.34}

3.3. Aufzählungen und@JsonValue

Eine weitere einfache Möglichkeit, die Marshalling-Ausgabe für eine Aufzählung zu steuern, ist die Verwendung der Annotation@JsonValueauf einem Getter:

public enum Distance {
    ...

    @JsonValue
    public String getMeters() {
        return meters;
    }
}

Was wir hier ausdrücken, ist, dassgetMeters() die tatsächliche Darstellung dieser Aufzählung ist. Das Ergebnis der Serialisierung ist also:

1609.34

3.4. Benutzerdefinierter Serializer für Enum

Vor Jackson 2.1.2 oder wenn für die Aufzählung noch weitere Anpassungen erforderlich sind, können wir eincustom Jackson serializer.verwenden. Zuerst müssen wir es definieren:

public class DistanceSerializer extends StdSerializer {

    public DistanceSerializer() {
        super(Distance.class);
    }

    public DistanceSerializer(Class t) {
        super(t);
    }

    public void serialize(Distance distance, JsonGenerator generator, SerializerProvider provider)
      throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeFieldName("name");
        generator.writeString(distance.name());
        generator.writeFieldName("unit");
        generator.writeString(distance.getUnit());
        generator.writeFieldName("meters");
        generator.writeNumber(distance.getMeters());
        generator.writeEndObject();
    }
}

Wir werden nun den Serializer auf die Klasse anwenden, die serialisiert werden soll:

@JsonSerialize(using = DistanceSerializer.class)
public enum TypeEnum { ... }

Was in ... endet:

{"name":"MILE","unit":"miles","meters":1609.34}

4. JSON für Enum deserialisieren

Definieren wir zunächst eine KlasseCitymit einem MitgliedDistance:

public class City {

    private Distance distance;
    ...
}

Als Nächstes werden die verschiedenen Möglichkeiten zum Deserialisieren einer JSON-Zeichenfolge in eine Aufzählung erläutert.

4.1. Standardverhalten

By default, Jackson will use the Enum name to deserialize from JSON.

Beispielsweise wird der JSON deserialisiert:

{"distance":"KILOMETER"}

Zu einemDistance.KILOMETER Objekt:

City city = new ObjectMapper().readValue(json, City.class);
assertEquals(Distance.KILOMETER, city.getDistance());

4.2. Verwenden von@JsonValue

Wir haben gelernt, wie Sie@JsonValue zum Serialisieren von Enums verwenden. Dieselbe Annotation können wir auch für die Deserialisierung verwenden. Dies ist möglich, weil Enum-Werte Konstanten sind.

Verwenden wir zunächst@JsonValue mit einer der Getter-Methoden -getMeters():

public enum Distance {
    ...

    @JsonValue
    public double getMeters() {
        return meters;
    }
}

Der Rückgabewert der MethodegetMeters()repräsentiert nun die Enum-Objekte. Wenn Sie den Beispiel-JSON deserialisieren, geschieht Folgendes:

{"distance":"0.0254"}

Jackson sucht nach dem Enum-Objekt mit einemgetMeters()-Rückgabewert von 0,0254. In diesem Fall lautet das ObjektDistance.INCH:

assertEquals(Distance.INCH, city.getDistance());

4.3. Verwenden von@JsonProperty

Die Annotation@JsonPropertywird für Aufzählungsinstanzen verwendet:

public enum Distance {
    @JsonProperty("distance-in-km")
    KILOMETER("km", 1000),
    @JsonProperty("distance-in-miles")
    MILE("miles", 1609.34);

    ...
}

Mit dieser Annotation könnenwe are simply telling Jackson to map the value of the @JsonProperty to the object annotated with this value.

Aufgrund der obigen Deklaration lautet die JSON-Beispielzeichenfolge:

{"distance": "distance-in-km"}

Wird dem ObjektDistance.KILOMETERzugeordnet:

assertEquals(Distance.KILOMETER, city.getDistance());

4.4. Verwenden von@JsonCreator

Jackson ruft mit@JsonCreator annotierte Methoden auf, um eine Instanz der einschließenden Klasse abzurufen.

Betrachten Sie die JSON-Darstellung:

{
    "distance": {
        "unit":"miles",
        "meters":1609.34
    }
}

Definieren wir nun die Factory-Methode vonforValues()mit der Annotation von@JsonCreator:

public enum Distance {

    @JsonCreator
    public static Distance forValues(@JsonProperty("unit") String unit,
      @JsonProperty("meters") double meters) {
        for (Distance distance : Distance.values()) {
            if (distance.unit.equals(unit) && Double.compare(distance.meters, meters) == 0) {
                return distance;
            }
        }

        return null;
    }

    ...
}

Beachten Sie die Verwendung der Annotation@JsonProperty, um die JSON-Felder mit den Methodenargumenten zu binden.

Wenn wir dann das JSON-Beispiel deserialisieren, erhalten wir das Ergebnis:

assertEquals(Distance.MILE, city.getDistance());

4.5. Verwenden eines benutzerdefiniertenDeserializer

Ein benutzerdefinierter Deserializer kann verwendet werden, wenn keine der beschriebenen Techniken verfügbar ist. Beispielsweise haben wir möglicherweise keinen Zugriff auf den Enum-Quellcode oder verwenden eine ältere Jackson-Version, die eine oder mehrere der bisher behandelten Anmerkungen nicht unterstützt.

Gemäßour custom deserialization article erstellen wir zunächst die Deserialisierungsklasse, um den im vorherigen Abschnitt bereitgestellten JSON zu deserialisieren:

public class CustomEnumDeserializer extends StdDeserializer {

    @Override
    public Distance deserialize(JsonParser jsonParser, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);

        String unit = node.get("unit").asText();
        double meters = node.get("meters").asDouble();

        for (Distance distance : Distance.values()) {

            if (distance.getUnit().equals(unit) && Double.compare(distance.getMeters(), meters) == 0) {
                return distance;
            }
        }

        return null;
    }
}

Als Nächstes verwenden wir die Annotation@JsonDeserializein der Aufzählung, um unseren benutzerdefinierten Deserializer anzugeben:

@JsonDeserialize(using = CustomEnumDeserializer.class)
public enum Distance {
   ...
}

Und unser Ergebnis ist:

assertEquals(Distance.MILE, city.getDistance());

5. Fazit

Dieser Artikel zeigt, wie Sieserialization and deserialization processes and formats of Java Enums besser kontrollieren können.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie inover on Github.