JsonCreator kullanarak aşırı yüklenmiş oluşturuculara sahip bir sınıfın serisini kaldırma


83

Jackson 1.9.10'u kullanarak bu sınıfın bir örneğini seriyi kaldırmaya çalışıyorum:

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

Bunu denediğimde aşağıdakileri alıyorum

Çakışan mülke dayalı içerik oluşturucular: zaten ... {interface org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}] karşılaşıldı ..., ek açıklamalar: {interface org.codehaus. jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator ()}]

Jackson'ı kullanarak aşırı yüklenmiş oluşturuculara sahip bir sınıfı seri durumdan çıkarmanın bir yolu var mı?

Teşekkürler


4
Cevabın işaret ettiği gibi, hayır, tek bir kurucu belirtmelisiniz. Sizin durumunuzda, çok sayıda argüman alan birini bırakın, bu gayet iyi çalışacaktır. "Eksik" bağımsız değişkenler boş (Nesneler için) veya varsayılan değer (ilkeller için) alacaktır.
StaxMan

Teşekkürler. Birden çok kurucuya izin vermek güzel bir özellik olacaktır. Aslında benim örneğim biraz uydurma. Kullanmaya çalıştığım nesnenin aslında tamamen farklı argüman listeleri var, biri normal olarak oluşturuldu, diğeri bir Throwable ile oluşturuldu ... Ne yapabileceğime bakacağım, belki boş bir kurucu ve alıcı / ayarlayıcı Atılabil
geejay

Evet, eminim güzel olurdu, ama kurallar farklı permütasyonlarla oldukça karmaşık hale gelebilir. Yeni işlevler, özellikler için RFE'leri dosyalamak her zaman mümkündür.
StaxMan

Yanıtlar:


119

Düzgün bir şekilde belgelenmemiş olsa da, her tür için yalnızca bir oluşturucuya sahip olabilirsiniz. Türünüzde istediğiniz kadar kurucuya sahip olabilirsiniz, ancak bunlardan sadece birinin @JsonCreatorüzerinde ek açıklama bulunmalıdır .


72

DÜZENLEME : Bakın, Jackson'ın geliştiricilerinin bir blog gönderisinde , 2.12'nin yapıcı enjeksiyonu ile ilgili gelişmeler görebileceği görülüyor. (Bu düzenlemenin yapıldığı tarihteki mevcut sürüm 2.11.1'dir)

Belirsiz 1 bağımsız değişkenli oluşturucularla sorunları çözme / hafifletme dahil olmak üzere Oluşturucu oluşturucuların otomatik algılamasını iyileştirin (yetki verme ve özellikler)


Bu, Jackson databind 2.7.0 için hala geçerlidir.

Jackson @JsonCreatoraçıklama 2.5 javadoc veya Jackson'un açıklamalar belgelere dilbilgisi ( kurucu lar ve fabrika yöntemi ler ) Bir yapabilmesi gerçekten inanalım işaretlemek birden fazla kurucuya.

Oluşturucuları ve fabrika yöntemlerini, ilişkili sınıfın yeni örneklerini somutlaştırmak için kullanılacak biri olarak tanımlamak için kullanılabilen işaretleyici ek açıklaması.

Yaratıcıların belirlendiği koda bakıldığında , Jackson aşırı yüklenmiş kurucularıCreatorCollector görmezden geliyor gibi görünüyor çünkü sadece kurucunun ilk argümanını kontrol ediyor .

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 tanımlanmış ilk kurucu yaratıcısıdır.
  • newOne aşırı yüklenmiş yapıcı yaratıcısıdır.

Bu, böyle bir kodun çalışmayacağı anlamına gelir

@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");

Ancak bu kod çalışacaktır:

@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");

Bu biraz karmaşıktır ve ileride kanıtı olmayabilir .


Dokümantasyon, nesne oluşturmanın nasıl çalıştığı konusunda belirsizdir; koddan topladığım kadarıyla, farklı yöntemleri karıştırmak mümkün:

Örneğin, bir statik fabrika yöntemi ile açıklanmış olabilir @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");

Çalışıyor ama ideal değil. JSON yani eğer Sonunda, örneğin anlamlı olamaz dinamik sonra belki bir çoklu açıklamalı kurucular ile çok daha zarif kulp yük değişimlerine bir temsilci kurucusunu kullanmak bakmak gerekir.

Ayrıca, Jackson'ın içerik oluşturucuları öncelik sırasına göre sıraladığını unutmayın , örneğin bu kodda:

// 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");

Bu sefer Jackson bir hata ortaya çıkarmayacak, ancak Jackson yalnızca delege kurucusunu kullanacak Phone(Map<String, Object> properties), yani Phone(@JsonProperty("value") String value)hiçbir zaman kullanılmayacak.


8
IMHO bu kabul edilen cevap olmalı çünkü güzel bir örnekle tam bir açıklama sağlıyor
matiou

7

Başarmaya çalıştığınız şeyi doğru yaparsam, bunu yapıcı aşırı yüklemesi olmadan çözebilirsiniz .

Yalnızca bir JSON veya Harita'da bulunmayan özniteliklere boş değerler koymak istiyorsanız, aşağıdakileri yapabilirsiniz:

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

Sorunuzu bulduğumda benim durumum buydu. Nasıl çözeceğimi anlamam biraz zaman aldı, belki de yapmaya çalıştığın buydu. @Brice çözümü benim için işe yaramadı.


1
En iyi anwer imho
Jakob

3

Biraz daha fazla iş yapmaktan çekinmezseniz, varlığı manuel olarak kaldırabilirsiniz:

@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");
            }
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.