जैक्सन enum Serializing और DeSerializer


225

मैं JAVA 1.6 और जैक्सन 1.9.9 का उपयोग कर रहा हूं

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;
    }
}

मैंने एक @JsonValue जोड़ा है, ऐसा लगता है कि यह उस काम को करता है जो इसमें ऑब्जेक्ट को क्रमबद्ध करता है:

{"event":"forgot password"}

लेकिन जब मैं deserialize करने की कोशिश मैं एक मिल

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

मुझे यहां क्या समझ नहीं आ रहा है?


4
क्या आपने कोशिश की है {"Event":"FORGOT_PASSWORD"}? घटना और FORGOT_PASSWORD दोनों पर कैप नोट करें।
ओल्डकुरमुडगेन


अगर आप अलग-अलग नामकरण सम्मेलन का अनुसरण करते हैं, तो getValueGetValue
गेट्टर

जवाबों:


287

Serializer / deserializer समाधान बताया द्वारा @xbakesx एक उत्कृष्ट एक अगर आप अपने दसगुणा पूरी तरह से करना चाहते हैं है enum अपने JSON प्रतिनिधित्व से वर्ग।

वैकल्पिक रूप से, यदि आप एक स्व-निहित समाधान पसंद करते हैं, तो एक कार्यान्वयन पर आधारित @JsonCreatorऔर @JsonValueएनोटेशन अधिक सुविधाजनक होगा।

उदाहरण के लिए @Stanley द्वारा निम्नलिखित का पूरा आत्म-निहित समाधान है (जावा 6, जैक्सन 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
    }
}

@ अस्तु, कृपया मेरे प्रश्न पर एक नज़र डालें, मुझे क्या याद आ रहा है stackoverflow.com/questions/30525986/enum-is-not-binding
प्रभजोत सिंह

25
शायद कुछ के लिए स्पष्ट है, लेकिन ध्यान दें कि @ JsonValue क्रमांकन के लिए और @ JsonCreator deserialization के लिए उपयोग किया जाता है। यदि आप दोनों नहीं कर रहे हैं तो आपको केवल एक या दूसरे की आवश्यकता होगी।
अक्बेलु

6
मैं वास्तव में सरल तथ्य के लिए इस समाधान को नापसंद करता हूं कि आप सच्चाई के दो स्रोतों का परिचय देते हैं। डेवलपर को दो स्थानों में नाम जोड़ने के लिए हमेशा याद रखना होगा। मैं एक ऐसे समाधान को प्राथमिकता देता हूं जो सिर्फ एक नक्शे के साथ एक एनम के आंतरिक को सजाने के बिना सही काम करता है।
mttdbrd

2
@ttdbrd सत्य को एकजुट करने के लिए इसके बारे में कैसे? gist.github.com/Scuilion/036c53fd7fee2de89701a95822c0fb60
केविन

1
स्थैतिक मानचित्र के बजाय आप YourEnum.values ​​() का उपयोग कर सकते हैं, जो आपका YourEnum का एरियर देता है और उस पर पुनरावृत्ति करता है
Valeriy K.

209

ध्यान दें कि जून 2015 में इस प्रतिबद्धता के अनुसार (जैक्सन 2.6.2 और ऊपर) अब आप बस लिख सकते हैं:

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

1
अच्छा समाधान। यह एक शर्म की बात है मैं २.६.० के साथ ड्रॉपवॉल्ड में फंस गया हूं:
क्लिंट ईस्टवुड

1
दुर्भाग्य से यह आपके एनम को स्ट्रिंग में परिवर्तित करने पर संपत्ति वापस नहीं करता है।
निकोलस

4
यह सुविधा 2.8 के बाद से हटा दी गई थी।
pqian

2
यह समाधान Enum पर क्रमबद्ध और deserialize दोनों के लिए काम करता है। 2.8 में परीक्षण किया गया।
डाउनहिल्स्की


88

आपको एक स्थिर फैक्टरी विधि बनानी चाहिए जो एकल तर्क लेती है और इसके साथ एनोटेट करती है @JsonCreator(जैक्सन 1.2 के बाद से उपलब्ध)

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

JsonCreator एनोटेशन के बारे में और अधिक पढ़ें यहाँ


10
यह सबसे साफ और सबसे संक्षिप्त समाधान है, बाकी सभी बॉयलरप्लेट के टन हैं जो सभी लागतों से बचाए जा सकते हैं (और चाहिए!)।
क्लिंट ईस्टवुड

4
@JSONValueक्रमबद्ध करने के लिए और @JSONCreatordeserialize करने के लिए।
चिरंजीब

@JsonCreator public static Event valueOf(int intValue) { ... }deserialize करने intके लिए Eventप्रगणक।
इडो 95

1
@ClintEastwood से बचा जाना चाहिए कि क्या अन्य समाधान इस बात पर निर्भर करते हैं कि आप सीरम से अलग serialzation / deserialization चिंताओं को अलग करना चाहते हैं या नहीं।
एएसए

44

वास्तविक उत्तर:

एनम के लिए डिफ़ॉल्ट डिसेरिएलाइज़र डिसेरिएलाइज़ करने के लिए उपयोग करता .name()है, इसलिए यह उपयोग नहीं कर रहा है @JsonValue। इसलिए @OldCurmudgeon ने बताया कि आपको मूल्य {"event": "FORGOT_PASSWORD"}से मेल खाने के लिए पास होना होगा .name()

एक अन्य विकल्प (यह मानते हुए कि आप लिखना और पढ़ना चाहते हैं कि मान एक ही हो) ...

और जानकारी:

जैक्सन के साथ क्रमांकन और deserialization प्रक्रिया का प्रबंधन करने का एक और तरीका है (अभी तक)। आप इन एनोटेशन को अपने स्वयं के कस्टम क्रमिक और डेज़राइज़र का उपयोग करने के लिए निर्दिष्ट कर सकते हैं:

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

फिर आपको लिखना है MySerializerऔर MyDeserializerइस तरह दिखना है:

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;
    }

}

अंतिम रूप से, विशेष रूप JsonEnumसे विधि के साथ क्रमबद्ध करने वाले किसी एनम के लिए ऐसा करने के लिए getYourValue(), आपका धारावाहिक और डेज़राइज़र इस तरह दिख सकता है:

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;
}

3
कस्टम (डी) धारावाहिक का उपयोग सादगी को मारता है (जो जैक्सन का उपयोग कर रहा है, btw के लिए लायक है), इसलिए वास्तव में भारी परिस्थितियों में इसकी आवश्यकता है। @JsonCreator का उपयोग करें, जैसा कि नीचे वर्णित है, और इस टिप्पणी की
दिमित्री ग्रायाज़िन

1
ओप्स प्रश्न में पेश की गई कुछ हद तक समस्या के लिए यह सॉलिटून सबसे अच्छा है। यहां वास्तविक मुद्दा यह है कि ओपी संरचित डेटा को एक रेंडर रूप में वापस करना चाहता है । यही है, वे डेटा वापस कर रहे हैं जिसमें पहले से ही उपयोगकर्ता के अनुकूल स्ट्रिंग शामिल है। लेकिन प्रदान किए गए फॉर्म को वापस पहचानकर्ता में बदलने के लिए, हमें कुछ कोड की आवश्यकता है जो परिवर्तन को उलट सकते हैं। हैकी स्वीकृत उत्तर परिवर्तन को संभालने के लिए एक मानचित्र का उपयोग करना चाहता है, लेकिन अधिक रखरखाव की आवश्यकता होती है। इस समाधान के साथ, आप नए प्रगणित प्रकार जोड़ सकते हैं और फिर आपके डेवलपर्स अपनी नौकरियों के साथ मिल सकते हैं।
mttdbrd 5

34

मुझे एक बहुत अच्छा और संक्षिप्त समाधान मिला है, विशेष रूप से उपयोगी है जब आप एनम वर्गों को संशोधित नहीं कर सकते हैं जैसा कि मेरे मामले में था। फिर आपको एक निश्चित सुविधा सक्षम के साथ एक कस्टम ObjectMapper प्रदान करना चाहिए। वे सुविधाएँ जैक्सन 1.6 के बाद से उपलब्ध हैं। तो आपको केवल toString()अपने एनम में विधि लिखने की आवश्यकता है ।

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);
    }
}

अधिक एनुम से संबंधित सुविधाएँ उपलब्ध हैं, यहाँ देखें:

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


10
निश्चित नहीं है कि आपको कक्षा का विस्तार करने की आवश्यकता क्यों है। आप ObjectMapper के उदाहरण पर इस सुविधा को सक्षम कर सकते हैं।
mttdbrd

+1 क्योंकि उन्होंने मुझे [READ | WRITE] _ENUMS_USING_TO_STRING को इंगित किया, जिसका उपयोग मैं स्प्रिंग एप्लिकेशन में कर सकता हूं
।yml

8

इसे इस्तेमाल करे।

public enum Event {

    FORGOT_PASSWORD("forgot password");

    private final String value;

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

    private Event() {
        this.value = this.name();
    }

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

6

आप किसी भी विशेषता के लिए डिसेरिएलाइज़ेशन को अनुकूलित कर सकते हैं।

import com.fasterxml.jackson.databind.annotation.JsonDeserializeसंसाधित किए जाने वाले विशेषता के लिए एनोटेशनजसनडेसरीलाइज़ ( ) का उपयोग करके अपने डिसेरिएलाइज़ वर्ग की घोषणा करें । यदि यह एक एनम है:

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

इस तरह से आपकी कक्षा का उपयोग विशेषता का वर्णन करने के लिए किया जाएगा। यह एक पूर्ण उदाहरण है:

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;
    }
}

नथानिएल फोर्ड, बेहतर हो गया?
फर्नांडो गोम्स

1
हां, यह एक बेहतर जवाब है; यह कुछ संदर्भ प्रदान करता है। मैं और भी आगे जाऊंगा, और इस बात पर चर्चा करूंगा कि इस तरह से वंशानुक्रम को जोड़ना ओपी की विशिष्ट बाधा को संबोधित करता है।
नथानिएल फोर्ड

5

विभिन्न दृष्टिकोण हैं जो आप किसी JSON ऑब्जेक्ट के एनरोमाइजेशन को पूरा करने के लिए ले सकते हैं। मेरी पसंदीदा शैली आंतरिक कक्षा बनाना है:

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

यहां एक और उदाहरण है जो नक्शे के बजाय स्ट्रिंग मानों का उपयोग करता है।

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

एक एनुम के संदर्भ में, @JsonValueअब (2.0 के बाद से) क्रमांकन के लिए काम करता है और डीरिएलाइज़ेशन के ।

के अनुसार के लिए जैक्सन-एनोटेशन जावाडोक@JsonValue :

ध्यान दें: जब जावा एनम के लिए उपयोग किया जाता है, तो एक अतिरिक्त विशेषता यह है कि एनोटेट विधि द्वारा लौटाया गया मान भी केवल JSON स्ट्रिंग से अनुक्रमित करने के लिए नहीं, बल्कि डीसर्विलाइज़ करने के लिए मूल्य माना जाता है। यह संभव है क्योंकि Enum मानों का सेट स्थिर है और मानचित्रण को परिभाषित करना संभव है, लेकिन POJO प्रकारों के लिए सामान्य रूप से नहीं किया जा सकता है; इस प्रकार, इसका उपयोग POJO डिसेरिएलाइज़ेशन के लिए नहीं किया जाता है।

इसलिए Eventजैकसन 2.0+ के साथ एनुम को उपरोक्त कार्यों (क्रमबद्धता और डिजिर्लाइज़ेशन दोनों के लिए) के रूप में एनोटेट किया गया है।


3

@JsonSerialize @JsonDeserialize का उपयोग करने के अलावा, आप ऑब्जेक्ट मैपर में SerializationFeature और DeserializationFeature (जैकसन बाइंडिंग) का भी उपयोग कर सकते हैं।

जैसे कि DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, जो डिफॉल्ट एनम टाइप देता है यदि प्रदान किया गया एनम वर्ग में परिभाषित नहीं है।


0

सबसे आसान तरीका जो मैंने पाया, वह है @ JsonFormat.Shape.OBJECT एनोटेशन एनम के लिए।

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

0

मेरे मामले में, यह वही हल है:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;

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

    DAILY(1),
    WEEKLY(2),
    ;

    private final int id;

    PeriodEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return this.name();
    }

    @JsonCreator
    public static PeriodEnum fromJson(@JsonProperty("name") String name) {
        return valueOf(name);
    }
}

निम्नलिखित जॅसन को सीरियलाइज़ और डिस्क्रिअलाइज़ करता है:

{
  "id": 2,
  "name": "WEEKLY"
}

मुझे उम्मीद है यह मदद करेगा!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.