JsonCreator का उपयोग करके अतिभारित कंस्ट्रक्टरों के साथ एक वर्ग को कैसे अलग करना है


83

मैं जैक्सन 1.9.10 का उपयोग करके इस वर्ग के उदाहरण का वर्णन करने की कोशिश कर रहा हूं:

public class Person {

@JsonCreator
public Person(@JsonProperty("name") String name,
        @JsonProperty("age") int age) {
    // ... person with both name and age
}

@JsonCreator
public Person(@JsonProperty("name") String name) {
    // ... person with just a name
}
}

जब मैं यह कोशिश करता हूं तो मुझे निम्नलिखित मिलता है

संपत्ति-आधारित रचनाकारों का विरोध: पहले से ही था ... {इंटरफ़ेस org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}], ... ... एनोटेशन: {इंटरफ़ेस org.codehaus। jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}]

क्या जैक्सन का उपयोग करके अतिभारित निर्माणकर्ताओं के साथ एक वर्ग को अलग करने का एक तरीका है?

धन्यवाद


4
जैसा कि उत्तर बताता है, नहीं, आपको एक और केवल एक निर्माता निर्दिष्ट करना होगा। आपके मामले में, जो एक से अधिक तर्क लेता है, उसे छोड़ दें, यह ठीक काम करेगा। "मिसिंग" तर्क अशक्त (वस्तुओं के लिए), या डिफ़ॉल्ट मान (आदिम के लिए) लेंगे।
StaxMan

धन्यवाद। हालांकि कई कंस्ट्रक्टरों की अनुमति देना एक अच्छी सुविधा होगी। दरअसल, मेरा उदाहरण थोड़ा विवादित है। मैं वास्तव में पूरी तरह से अलग तर्क सूचियों का उपयोग करने की कोशिश कर रहा हूं, एक सामान्य रूप से बनाई गई है, दूसरा एक थ्रोबेबल के साथ बनाई गई है ... मैं देखूंगा कि मैं क्या कर सकता हूं, शायद एक खाली कंस्ट्रक्टर और गेट्टर / सेटर के लिए। थिरबल
गीज

हाँ, मुझे यकीन है कि यह अच्छा होगा, लेकिन नियम अलग-अलग क्रमपरिवर्तन के साथ जटिल हो सकते हैं। नई कार्यक्षमता, सुविधाओं के लिए हमेशा RFE फाइल करना संभव है।
StaxMan

जवाबों:


119

हालांकि इसकी सही ढंग से प्रलेखित नहीं की गई है, आप प्रति प्रकार केवल एक निर्माता हो सकते हैं। आपके पास अपने प्रकार में जितने चाहें उतने कंस्ट्रक्टर हो सकते हैं, लेकिन उनमें से केवल एक पर ही @JsonCreatorएनोटेशन होना चाहिए ।


72

EDIT : जैक्सन के अनुरक्षकों द्वारा एक ब्लॉग पोस्ट में, यह लगता है कि 2.12 में कंस्ट्रक्टर इंजेक्शन के संबंध में सुधार देखने को मिल सकते हैं। (इस एडिट के समय वर्तमान संस्करण 2.11.1 है)

कंस्ट्रक्टर रचनाकारों के ऑटो-डिटेक्शन में सुधार करें, जिसमें अस्पष्ट 1-तर्क निर्माणकर्ताओं (गुणों बनाम प्रतिनिधि) के साथ समस्याओं को हल करना / हल करना शामिल है


यह अभी भी जैक्सन डेटाबैन्ड 2.7.0 के लिए सही है।

जैक्सन @JsonCreatorएनोटेशन 2.5 जैवाडोक या जैक्सन एनोटेशन प्रलेखन व्याकरण ( निर्माता एस और फैक्टरी विधि) रों ) दें वास्तव में विश्वास है कि एक कर सकते हैं निशान कई निर्माताओं।

मार्कर एनोटेशन जिसका उपयोग निर्माणकर्ता और कारखाने के तरीकों को परिभाषित करने के लिए किया जा सकता है ताकि संबंधित वर्ग के नए उदाहरणों का तुरंत उपयोग किया जा सके।

उस कोड को देखते हुए जहां रचनाकारों की पहचान की जाती है, ऐसा लगता है कि जैक्सन ओवरलोड निर्माणकर्ताओं कीCreatorCollector अनदेखी कर रहा है क्योंकि यह केवल निर्माता के पहले तर्क की जांच करता है

Class<?> oldType = oldOne.getRawParameterType(0);
Class<?> newType = newOne.getRawParameterType(0);

if (oldType == newType) {
    throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex]
           +" creators: already had explicitly marked "+oldOne+", encountered "+newOne);
}
  • oldOne पहला रचनाकार रचनाकार है।
  • newOne अतिभारित रचनाकार है।

इसका मतलब है कि कोड की तरह यह काम नहीं करेगा

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    this.country = "";
}

@JsonCreator
public Phone(@JsonProperty("country") String country, @JsonProperty("value") String value) {
    this.value = value;
    this.country = country;
}

assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336"); // raise error here
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");

लेकिन यह कोड काम करेगा:

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    enabled = true;
}

@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
    this.value = value;
    this.enabled = enabled;
}

assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

यह थोड़ा हैकरी है और भविष्य का प्रमाण नहीं हो सकता है


प्रलेखन अस्पष्ट है कि ऑब्जेक्ट निर्माण कैसे काम करता है; हालांकि मैं कोड से क्या इकट्ठा करता हूं, यह है कि विभिन्न तरीकों को मिलाना संभव है:

उदाहरण के लिए किसी के पास एक स्थिर कारखाना विधि हो सकती है, जिसके साथ एनोटेट किया गया है @JsonCreator

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    enabled = true;
}

@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
    this.value = value;
    this.enabled = enabled;
}

@JsonCreator
public static Phone toPhone(String value) {
    return new Phone(value);
}

assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

यह काम करता है लेकिन यह आदर्श नहीं है। अंत में, यह समझ में आ सकता है, उदाहरण के लिए, यदि JSON वह गतिशील है, तो शायद किसी एनोटेट निर्माणकर्ताओं के मुकाबले पेलोड विविधताओं को संभालने के लिए प्रतिनिधि प्रतिनिधि का उपयोग करना चाहिए।

यह भी ध्यान दें कि जैक्सन प्राथमिकता से रचनाकारों का आदेश देता है , उदाहरण के लिए इस कोड में:

// Simple
@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
}

// more
@JsonCreator
public Phone(Map<String, Object> properties) {
    value = (String) properties.get("value");
    
    // more logic
}

assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

इस बार जैक्सन कोई त्रुटि नहीं करेगा, लेकिन जैक्सन केवल प्रतिनिधि निर्माणकर्ता का उपयोग करेगा Phone(Map<String, Object> properties), जिसका अर्थ है कि इसका Phone(@JsonProperty("value") String value)उपयोग कभी नहीं किया जाता है।


8
IMHO यह स्वीकृत उत्तर होना चाहिए क्योंकि यह
मतिउ

7

अगर मुझे वह सही मिला है जिसे आप प्राप्त करने की कोशिश कर रहे हैं, तो आप इसे बिना निर्माण अधिभार के हल कर सकते हैं

यदि आप केवल JSON या मानचित्र में मौजूद विशेषताओं में शून्य मान रखना चाहते हैं, तो आप निम्न कार्य कर सकते हैं:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private String name;
    private Integer age;
    public static final Integer DEFAULT_AGE = 30;

    @JsonCreator
    public Person(
        @JsonProperty("name") String name,
        @JsonProperty("age") Integer age) 
        throws IllegalArgumentException {
        if(name == null)
            throw new IllegalArgumentException("Parameter name was not informed.");
        this.age = age == null ? DEFAULT_AGE : age;
        this.name = name;
    }
}

यह मेरा मामला था जब मुझे आपका सवाल मिला। मुझे यह पता लगाने में कुछ समय लगा कि इसे कैसे हल किया जाए, शायद यही आप करने की कोशिश कर रहे थे। @Brice समाधान मेरे लिए काम नहीं किया।


1
बेस्ट एवेर इम्हो
जैकब

3

यदि आपको थोड़ा और काम करने में कोई दिक्कत नहीं है, तो आप इकाई को मैन्युअल रूप से निष्क्रिय कर सकते हैं:

@JsonDeserialize(using = Person.Deserializer.class)
public class Person {

    public Person(@JsonProperty("name") String name,
            @JsonProperty("age") int age) {
        // ... person with both name and age
    }

    public Person(@JsonProperty("name") String name) {
        // ... person with just a name
    }

    public static class Deserializer extends StdDeserializer<Person> {
        public Deserializer() {
            this(null);
        }

        Deserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            JsonNode node = jp.getCodec().readTree(jp);
            if (node.has("name") && node.has("age")) {
                String name = node.get("name").asText();
                int age = node.get("age").asInt();
                return new Person(name, age);
            } else if (node.has("name")) {
                String name = node.get("name").asText();
                return new Person("name");
            } else {
                throw new RuntimeException("unable to parse");
            }
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.