it-swarm.com.de

Jackson Enum Serializing und DeSerializer

Ich benutze Java 1.6 und Jackson 1.9.9 Ich habe eine Aufzählung

public enum Event {
    FORGOT_PASSWORD("forgot password");

    private final String value;

    private Event(final String description) {
        this.value = description;
    }

    @JsonValue
    final String value() {
        return this.value;
    }
}

Ich habe ein @JsonValue hinzugefügt, dies scheint den Job zu erledigen, in den es das Objekt serialisiert:

{"event":"forgot password"}

aber wenn ich versuche zu deserialisieren bekomme ich eine

Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.globalrelay.gas.appsjson.authportal.Event from String value 'forgot password': value not one of declared Enum instance names

Was vermisse ich hier?

197
pookieman

Die Serializer/Deserializer-Lösung, auf die @ xbakesx hingewiesen hat, eignet sich hervorragend, wenn Sie die Klasse enum vollständig entkoppeln möchten von seiner JSON-Darstellung.

Wenn Sie eine in sich geschlossene Lösung bevorzugen, können Sie auch eine Implementierung basierend auf @JsonCreator und @JsonValue Anmerkungen wären bequemer.

Verwenden Sie also das Beispiel von @ Stanley , um eine vollständige eigenständige Lösung zu erhalten (Java 6, Jackson 1.9):

public enum DeviceScheduleFormat {

    Weekday,
    EvenOdd,
    Interval;

    private static Map<String, DeviceScheduleFormat> namesMap = new HashMap<String, DeviceScheduleFormat>(3);

    static {
        namesMap.put("weekday", Weekday);
        namesMap.put("even-odd", EvenOdd);
        namesMap.put("interval", Interval);
    }

    @JsonCreator
    public static DeviceScheduleFormat forValue(String value) {
        return namesMap.get(StringUtils.lowerCase(value));
    }

    @JsonValue
    public String toValue() {
        for (Entry<String, DeviceScheduleFormat> entry : namesMap.entrySet()) {
            if (entry.getValue() == this)
                return entry.getKey();
        }

        return null; // or fail
    }
}
254

Beachten Sie, dass Sie ab this commit im Juni 2015 (Jackson 2.6.2 und höher) einfach schreiben können:

public enum Event {
    @JsonProperty("forgot password")
    FORGOT_PASSWORD;
}
179
tif

Sie sollten eine statische Factory-Methode erstellen, die ein einziges Argument verwendet und es mit @JsonCreator Kommentiert (verfügbar seit Jackson 1.2).

@JsonCreator
public static Event forValue(String value) { ... }

Lesen Sie mehr über JsonCreator Annotation hier .

84
Stanley

Aktuelle Antwort:

Der Standard-Deserializer für Aufzählungen verwendet .name() zum Deserialisieren, daher wird kein @JsonValue Verwendet. Wie @OldCurmudgeon hervorhob, müssten Sie {"event": "FORGOT_PASSWORD"} Übergeben, um dem .name() -Wert zu entsprechen.

Eine andere Option (vorausgesetzt, Sie möchten, dass Schreib- und Lesewerte für JSON identisch sind) ...

Mehr Info:

Es gibt (noch) eine andere Möglichkeit, den Serialisierungs- und Deserialisierungsprozess mit Jackson zu verwalten. Sie können diese Anmerkungen angeben, um Ihren eigenen benutzerdefinierten Serializer und Deserializer zu verwenden:

@JsonSerialize(using = MySerializer.class)
@JsonDeserialize(using = MyDeserializer.class)
public final class MyClass {
    ...
}

Dann musst du MySerializer und MyDeserializer schreiben, die so aussehen:

MySerializer

public final class MySerializer extends JsonSerializer<MyClass>
{
    @Override
    public void serialize(final MyClass yourClassHere, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException
    {
        // here you'd write data to the stream with gen.write...() methods
    }

}

MyDeserializer

public final class MyDeserializer extends org.codehaus.jackson.map.JsonDeserializer<MyClass>
{
    @Override
    public MyClass deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException
    {
        // then you'd do something like parser.getInt() or whatever to pull data off the parser
        return null;
    }

}

Als letztes, insbesondere wenn Sie dies für eine Aufzählung JsonEnum tun, die mit der Methode getYourValue() serialisiert wird, könnte Ihr Serializer und Deserializer folgendermaßen aussehen:

public void serialize(final JsonEnum enumValue, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException
{
    gen.writeString(enumValue.getYourValue());
}

public JsonEnum deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException
{
    final String jsonValue = parser.getText();
    for (final JsonEnum enumValue : JsonEnum.values())
    {
        if (enumValue.getYourValue().equals(jsonValue))
        {
            return enumValue;
        }
    }
    return null;
}
40
xbakesx

Ich habe eine sehr nette und prägnante Lösung gefunden, die insbesondere dann nützlich ist, wenn Sie keine Enum-Klassen wie in meinem Fall ändern können. Anschließend sollten Sie einen benutzerdefinierten ObjectMapper mit einer bestimmten aktivierten Funktion bereitstellen. Diese Funktionen sind seit Jackson 1.6 verfügbar. Sie müssen also nur die Methode toString() in Ihre Aufzählung schreiben.

public class CustomObjectMapper extends ObjectMapper {
    @PostConstruct
    public void customConfiguration() {
        // Uses Enum.toString() for serialization of an Enum
        this.enable(WRITE_ENUMS_USING_TO_STRING);
        // Uses Enum.toString() for deserialization of an Enum
        this.enable(READ_ENUMS_USING_TO_STRING);
    }
}

Es stehen weitere Funktionen zur Verfügung, die sich auf die Aufzählung beziehen. Siehe hier:

https://github.com/FasterXML/jackson-databind/wiki/Serialization-Featureshttps://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features

30
lagivan

Sie können die Deserialisierung für jedes Attribut anpassen.

Deklarieren Sie Ihre Deserialize-Klasse mit der Annotation JsonDeserialize (import com.fasterxml.jackson.databind.annotation.JsonDeserialize) für das Attribut, das verarbeitet werden soll. Wenn dies eine Aufzählung ist:

@JsonDeserialize(using = MyEnumDeserialize.class)
private MyEnum myEnum;

Auf diese Weise wird Ihre Klasse zum Deserialisieren des Attributs verwendet. Dies ist ein vollständiges Beispiel:

public class MyEnumDeserialize extends JsonDeserializer<MyEnum> {

    @Override
    public MyEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        MyEnum type = null;
        try{
            if(node.get("attr") != null){
                type = MyEnum.get(Long.parseLong(node.get("attr").asText()));
                if (type != null) {
                    return type;
                }
            }
        }catch(Exception e){
            type = null;
        }
        return type;
    }
}
6
Fernando Gomes

Versuche dies.

 
 öffentliches Enum-Ereignis {
 
 FORGOT_PASSWORD ("Passwort vergessen"); 
 
 privater endgültiger String-Wert; 
 
 privates Ereignis (endgültige Zeichenfolgenbeschreibung) {
 this.value = description; 
} 
 
 privates Ereignis () {
 this.value = this.name (); 
} 
 
 @JsonValue 
 final String value () {
 Diesen Wert zurückgeben ; 
} 
} 
4
Durga

Es gibt verschiedene Ansätze, mit denen Sie die Deserialisierung eines JSON-Objekts für eine Aufzählung durchführen können. Mein Lieblingsstil ist es, eine innere Klasse zu bilden:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;

import Java.util.Arrays;
import Java.util.Map;
import Java.util.function.Function;
import Java.util.stream.Collectors;

import static com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT;

@JsonFormat(shape = OBJECT)
public enum FinancialAccountSubAccountType {
  MAIN("Main"),
  MAIN_DISCOUNT("Main Discount");

  private final static Map<String, FinancialAccountSubAccountType> ENUM_NAME_MAP;
  static {
    ENUM_NAME_MAP = Arrays.stream(FinancialAccountSubAccountType.values())
      .collect(Collectors.toMap(
        Enum::name,
        Function.identity()));
  }

  private final String displayName;

  FinancialAccountSubAccountType(String displayName) {
    this.displayName = displayName;
  }

  @JsonCreator
  public static FinancialAccountSubAccountType fromJson(Request request) {
    return ENUM_NAME_MAP.get(request.getCode());
  }

  @JsonProperty("name")
  public String getDisplayName() {
    return displayName;
  }

  private static class Request {
    @NotEmpty(message = "Financial account sub-account type code is required")
    private final String code;
    private final String displayName;

    @JsonCreator
    private Request(@JsonProperty("code") String code,
                    @JsonProperty("name") String displayName) {
      this.code = code;
      this.displayName = displayName;
    }

    public String getCode() {
      return code;
    }

    @JsonProperty("name")
    public String getDisplayName() {
      return displayName;
    }
  }
}
4
Sam Berry

Hier ist ein weiteres Beispiel, das Zeichenfolgenwerte anstelle einer Zuordnung verwendet.

public enum Operator {
    EQUAL(new String[]{"=","==","==="}),
    NOT_EQUAL(new String[]{"!=","<>"}),
    LESS_THAN(new String[]{"<"}),
    LESS_THAN_EQUAL(new String[]{"<="}),
    GREATER_THAN(new String[]{">"}),
    GREATER_THAN_EQUAL(new String[]{">="}),
    EXISTS(new String[]{"not null", "exists"}),
    NOT_EXISTS(new String[]{"is null", "not exists"}),
    MATCH(new String[]{"match"});

    private String[] value;

    Operator(String[] value) {
        this.value = value;
    }

    @JsonValue
    public String toStringOperator(){
        return value[0];
    }

    @JsonCreator
    public static Operator fromStringOperator(String stringOperator) {
        if(stringOperator != null) {
            for(Operator operator : Operator.values()) {
                for(String operatorString : operator.value) {
                    if (stringOperator.equalsIgnoreCase(operatorString)) {
                        return operator;
                    }
                }
            }
        }
        return null;
    }
}
4
Gremash

Im Kontext einer Aufzählung mit @JsonValue funktioniert jetzt (seit 2.0) für die Serialisierung und Deserialisierung.

Nach den jackson-annotations javadoc für @JsonValue :

ANMERKUNG: Wenn Sie Java enums verwenden, besteht eine zusätzliche Funktion darin, dass der von der mit Annotationen versehenen Methode zurückgegebene Wert auch als der zu deserialisierende Wert angesehen wird und nicht nur als JSON-String, unter dem serialisiert werden soll. Dies ist möglich, da Die Menge der Enum-Werte ist konstant und es ist möglich, das Mapping zu definieren, kann jedoch im Allgemeinen nicht für POJO-Typen durchgeführt werden, da dies nicht für die POJO-Deserialisierung verwendet wird.

Das Event -Enum wie oben beschrieben zu haben, funktioniert also (sowohl für die Serialisierung als auch für die Deserialisierung) mit Jackson 2.0+.

3
Brice Roncace

Neben der Verwendung von @JsonSerialize @JsonDeserialize können Sie auch SerializationFeature und DeserializationFeature (Jackson-Bindung) in der Objektzuordnung verwenden.

B. DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, die den Standard-Aufzählungstyp angeben, wenn der angegebene nicht in der Aufzählungsklasse definiert ist.

2
Yuantao

Der einfachste Weg, den ich gefunden habe, ist die Verwendung der Annotation @ JsonFormat.Shape.OBJECT für die Aufzählung.

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum{
    ....
}
0
yrazlik