Deserialisieren der JSON-Zeichenfolge zum Aufzählen

Nov 23 2020

Ich habe den JSON wie folgt aussehen:

{
  "name": "john",
  "options": {
    "test": 1,
    "operation": "op1",     // I need to deserialize this option to enum.
    ...
    // any number of options
  }
}

und ich habe die Klasse sieht wie folgt aus:

public class Info {
    public String name;
    @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
    public Map<String, Object> options;
}

public enum OperationEnum {OP1, OP2, OP3}

Wie kann ich deserialisieren, optionsum operationwie enumund testzu kartierenInteger

Antworten

1 Matthew Nov 23 2020 at 23:30

Sie sollten in der Lage sein, @JsonAnySetterIhnen hier zu helfen:

public class Info {
    public static class Options {
      public int test;
      public OperationEnum operation;
    }

    public String name;
    public Options options;
    private final Map<String, Object> properties = new HashMap<>();

    @JsonAnySetter
    public void add(String key, String value) {
        properties.put(key, value);
    }

}

public enum OperationEnum {OP1, OP2, OP3}

Hier gibt es eine gute Zusammenfassung: https://www.concretepage.com/jackson-api/jackson-jsonanygetter-and-jsonanysetter-example

Matthew Nov 23 2020 at 22:08

Warum nicht so:


public class Info {
    public static class Options {
      public int test;
      public OperationEnum operation;
    }

    public String name;
    public Options options;
}

public enum OperationEnum {OP1, OP2, OP3}

Dann sollte es JustWork sein!

Elzzz Nov 23 2020 at 22:16

Die JSON-Struktur, in der alle Optionen (verschiedener Art) wie gespeichert werden, Map<String, Object> options;erzwingt eine zusätzliche Analyse, da der Werttyp anhand der Bezeichnung (wie "operation"und OperationEnumKlasse) identifiziert wird. Hier sind zwei Ansätze mit ObjectMapper.readValueundEnum.valueOf

Wenn es möglich ist, ziehen Sie flexiblere Lösungen wie die von @Matthew in Betracht

class Demo {
    public static void main(String[] args) throws Exception {
        ObjectMapper om = new ObjectMapper();
        Info info = om.readValue(jsonValue, Info.class);
        OperationEnum operationM = om.readValue("\"" + info.options.get("operation") + "\"", OperationEnum.class);
        System.out.println(operationM);

        OperationEnum operationE = OperationEnum.valueOf(info.options.get("operation").toString());
        System.out.println(operationE);
    }

    static String jsonValue = "{\n" +
            "  \"name\": \"john\",\n" +
            "  \"options\": {\n" +
            "    \"test\": 1,\n" +
            "    \"operation\": \"OP1\" \n" +
            "  }\n" +
            "}";
}
MichałZiober Nov 25 2020 at 05:59

Eine einfache Lösung besteht darin, die MapInstanz mit einer Klasse zu versehen und zusätzliche Methoden für jede nicht reguläre Eigenschaft zu implementieren. Sie können @JsonAnySetterAnmerkungen verwenden, um alle key-valuePaare zu speichern .

Siehe Beispiel unten:

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

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

public class JsonOptionsWithCustomEnumApp {
    public static void main(String[] args) throws IOException {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = new ObjectMapper();
        Info info = mapper.readValue(jsonFile, Info.class);
        Options options = info.getOptions();
        System.out.println(options.toMap());
        System.out.println(options.getOperation());
    }
}

class Info {
    private String name;
    private Options options;

    //getters, setters
}

class Options {
    private Map<String, Object> values = new LinkedHashMap<>();

    @JsonAnySetter
    private void setValues(String key, Object value) {
        values.put(key, value);
    }

    public OperationEnum getOperation() {
        Object operation = values.get("operation");
        if (operation == null) {
            return null;
        }
        return Arrays.stream(OperationEnum.values())
            .filter(op -> op.name().equalsIgnoreCase(operation.toString()))
            .findFirst()
            .orElse(null);
    }

    public Map<String, Object> toMap() {
        return values;
    }
}

enum OperationEnum {OP1, OP2, OP3}

Der obige Code druckt:

{test=1, operation=op1}
OP1