Jackson ObjectMapper - Geben Sie die Serialisierungsreihenfolge der Objekteigenschaften an

80

Ich implementiere einen RESTful-Webdienst, bei dem der Benutzer zusammen mit der Anfrage ein signiertes Bestätigungstoken senden muss, damit ich sicherstellen kann, dass die Anfrage nicht von einem Zwischenhändler manipuliert wurde. Meine aktuelle Implementierung ist wie folgt.

Das Verifikationstoken ist ein VerifData-Objekt, das in einen String serialisiert und dann gehasht und verschlüsselt wird.

class VerifData {
    int prop1;
    int prop2;
}

In meinem Dienst habe ich Daten, die serialisiert werden sollen, in eine Instanz von VerifData eingefügt und sie dann mit Jackson ObjectMapper serialisiert und zusammen mit dem Verifikationstoken an die Verifizierungs-Engine weitergeleitet.

VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);

Es scheint jedoch, dass sich bei jedem Start des Anwendungscontainers die Reihenfolge der Eigenschaften ändert, die von ObjectMapper einer Zeichenfolge zugeordnet werden.

Bsp.: Einmal wäre es

{"prop1":12345,"prop2":67890}

und ein anderes Mal wäre es

{"prop2":67890,"prop1":12345}

Wenn der Client die VerifData-Instanz als in den ersten String serialisiert hat, besteht eine 50% ige Wahrscheinlichkeit, dass sie fehlschlägt, obwohl sie korrekt ist.

Gibt es eine Möglichkeit, dies zu umgehen? Kann ich die Reihenfolge der Eigenschaften festlegen, die von ObjectMapper zugeordnet werden sollen (wie in aufsteigender Reihenfolge)? Oder gibt es eine andere Möglichkeit, diesen Überprüfungsschritt am besten umzusetzen? Sowohl Client- als auch Server-Implementierungen werden von mir entwickelt. Ich verwende die Java-Sicherheits-API zum Signieren und Überprüfen.

Lizzy
quelle

Antworten:

80

Aus der Dokumentation zu Jackson Annotations :

// ensure that "id" and "name" are output before other properties
@JsonPropertyOrder({ "id", "name" })

// order any properties that don't have explicit setting using alphabetic order
@JsonPropertyOrder(alphabetic=true)
wgitscht
quelle
über den Link "Alle Codehaus-Dienste wurden beendet"
Andrew Norman
2
Wie unten erwähnt, finden Sie unter stackoverflow.com/a/46267506/2089674 , wenn Sie die Reihenfolge überall anwenden möchten, anstatt ein bestimmtes Objekt zu serialisieren .
user2089674
89

Die Anmerkungen sind nützlich, können aber überall schmerzhaft sein. Sie können Ihren gesamten ObjectMapper so konfigurieren, dass er auf diese Weise funktioniert

Aktuelle Jackson-Versionen: objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

Ältere Jackson-Versionen: objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

Duncan McGregor
quelle
Dies ist nicht ganz richtig (oder funktioniert zumindest nicht in allen Szenarien). Ein vollständiges Beispiel für etwas, das funktioniert, finden Sie unter stackoverflow.com/a/46267506/2089674 .
user2089674
2
@ user2089674 - Ihr Beispiel funktioniert nur für Map-Schlüssel. Das OP forderte eine Lösung für Objektfelder an.
Dave
9

In Spring Boot können Sie dieses Verhalten global hinzufügen, indem Sie Ihrer ApplicationEinstiegspunktklasse Folgendes hinzufügen :

  @Bean
  public Jackson2ObjectMapperBuilder objectMapperBuilder() {

    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

    return builder;
  }
Gary Rowe
quelle
9

Es gibt einen einfacheren Weg in Spring Boot, indem Sie eine Eigenschaft angeben ( application.propertieszum Beispiel:

spring.jackson.mapper.sort_properties_alphabetically=true
Wim Deblauwe
quelle
8

Verwenden Sie in Jackson 2.x, das Sie wahrscheinlich heute verwenden, Folgendes:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

Wenn Sie sich für das Aussehen interessieren, können Sie dies auch berücksichtigen SerializationFeature.INDENT_OUTPUT.

Beachten Sie, dass Sie Karten oder Objekte serialisieren müssen, damit dies korrekt sortiert wird. Wenn Sie JsonNodebeispielsweise (von readTree) serialisieren , wird dies nicht richtig eingerückt.

Beispiel

import com.fasterxml.jackson.databind.*;

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);

String input = "{\"hello\": {\"cruel\" : \"world\"} }";
Object pojo = mapper.readValue(input, Object.class);
System.out.println(mapper.writeValueAsString(pojo));

Ergebnisse in:

{
  "hello" : {
    "cruel" : "world"
  }
}
user2089674
quelle
6
Es gibt nur einen Karteneintrag. Ich kann daher die Reihenfolge nicht sehen.
Ben
7

Die folgenden 2 ObjectMapper-Konfigurationen sind erforderlich:

ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
oder
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)

Definiert die Eigenschaftsserialisierungsreihenfolge, die für POJO-Felder verwendet wird.
Hinweis : Gilt nicht für die java.util.MapSerialisierung!

und

ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
oder
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)

Funktion, die bestimmt, ob java.util.MapEinträge vor der Serialisierung zuerst nach Schlüssel sortiert werden


Spring Boot Konfigurationsbeispiel (yaml):

spring:
  jackson:
    mapper:
      SORT_PROPERTIES_ALPHABETICALLY: true
    serialization:
      ORDER_MAP_ENTRIES_BY_KEYS: true
Zauberkünstler
quelle
3

Aus Duncan McGregors Antwort: Es ist besser, es so zu verwenden:

objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

da MapperFeature für XMLs ist und mit Jackson-Datenbindung geliefert wird, die nicht benötigt wird ...

Nemes Nisano
quelle
0

Anstatt das Flag-Argument zu verwenden:

objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
Leon
quelle
0

Sie können das Mix-In verwenden und die Reihenfolge der Eigenschaften nach Belieben festlegen:

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public final class ObjectMapperUtils {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.addMixIn(Object.class, IdFirst.class);
    }

    @Bean
    public ObjectMapper objectMapper() {
        return MAPPER;
    }

    @JsonPropertyOrder({"id", "...", "..."})
    private abstract static class IdFirst {}

}
Marcus Vinícius Voltolim
quelle