Jackson ilkel boole alanını 'eşittir'i kaldırarak yeniden adlandırıyor


93

Bu bir kopya olabilir. Ama Sorunuma bir çözüm bulamıyorum.

Sınıfım var

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

Getiriciler ve ayarlayıcılar Eclipse tarafından oluşturulur.

Başka bir sınıfta değeri true olarak ayarlıyorum ve JSON dizesi olarak yazıyorum.

System.out.println(new ObjectMapper().writeValueAsString(myResponse));

JSON'da anahtar olarak geliyor {"success": true}.

Anahtarın isSuccesskendisi olmasını istiyorum . Jackson serileştirme sırasında setter yöntemini kullanıyor mu? Anahtar nasıl alan adı haline getirilir?


1
Mülkünüzün adı uygunsa, isSuccessyöntem adı isIsSuccessbence olmalı
Jens

Anlıyorum. SetSuccess Eclipse tarafından üretildiği için daha iyi olduğunu düşündüm . (Bir standardın ardından)
iCode

Yanıtlar:


119

Bu biraz geç bir cevap, ancak bu sayfaya gelen diğer herkes için faydalı olabilir.

Jackson'ın JSON'a serileştirirken kullanacağı adı değiştirmenin basit bir çözümü @JsonProperty açıklamasını kullanmaktır , böylece örneğiniz şöyle olur:

public class MyResponse implements Serializable {

    private boolean isSuccess;

    @JsonProperty(value="isSuccess")        
    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean isSuccess) {
        this.isSuccess = isSuccess;
    }
}

Bu daha sonra JSON olarak serileştirilir {"isSuccess":true}, ancak alıcı yöntem adınızı değiştirmek zorunda kalmama avantajına sahiptir.

Bu durumda, ek açıklamayı @JsonProperty("isSuccess")yalnızca tek valueöğeye sahip olduğu için yazabileceğinizi unutmayın.


Üçüncü parti bağımlılıklardan geldiği için sınıf bana ait olmadığı için bu yöntem benim durumum için çalışmayacak. Böyle bir durum için aşağıdaki cevabıma bakınız.
edmundpie

4
Jackson ile spring boot kullanıyorum ancak iki alan elde ediyorum, biri "başarı" ve diğeri "isSuccess" ve ilkel olmayan Boolean'ı yalnızca bir alan "isSuccess" ten kullandığımda
Vishal Singla

@VishalSingla Aynı sorunu yaşıyorum, bu çözüm Spring Boot'da iki alan oluşturuyor
Aron Fiechter

22

Geçenlerde bu sorunla karşılaştım ve bulduğum şey bu. Jackson, kendisine ilettiğiniz herhangi bir sınıfı alıcılar ve ayarlayıcılar için inceleyecek ve bu yöntemleri serileştirme ve seriyi kaldırma için kullanacaktır. Bu yöntemlerde "get", "is" ve "set" komutlarından sonra gelenler JSON alanı için anahtar olarak kullanılacaktır (getIsValid ve setIsValid için "isValid").

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

    public void setIsValid(boolean isValid) {
        this.isValid = isValid;
    }
} 

Benzer şekilde "isSuccess", "isIsSuccess" veya "getIsSuccess" olarak yeniden adlandırılmadıkça "başarılı" olacaktır.

Daha fazlasını buradan okuyun: http://www.citrine.io/blog/2015/5/20/jackson-json-processor


6
isValid, java'da boolean veri türü için doğru adlandırma kuralı değildir. geçerli olmalı ve isValid (), setValid ()
vels4j

2
ama tam olarak böyle olması gerekmiyor mu? Kongre mi? Varsa, JSON alanları olarak alıcı adlarını kullandığını söyleyen Jackson referansına bağlanabilir misiniz? Yoksa bunun kötü bir tasarım seçimi olduğunu mu düşünüyorsunuz?
Abhinav Vishak

2
Keşke bunun için bir uyarı olsaydı
RyPope

@ vels4j Çok özel uygulamalarla uğraşırken adlandırma kuralları pencereden dışarı çıkar.
Dragas

13

Aşağıdaki her iki ek açıklamayı kullanmak, JSON çıktısını şunları içermeye zorlar is_xxx:

@get:JsonProperty("is_something")
@param:JsonProperty("is_something")

Bu sorunun en iyi cevabı budur.
dustinevan

1
Bu Java mı? Belki Kotlin'dir?
spottedmahn

5

Aşağıdaki gibi yapılandırabilirsiniz ObjectMapper:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
            @Override
            public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
            {
                if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
                        && method.getName().startsWith("is")) {
                    return method.getName();
                }
                return super.nameForGetterMethod(config, method, defaultName);
            }
        });

1
Bunu konfigürasyon yoluyla çözmeye çalışman hoşuma gidiyor. Ancak, bu yalnızca boole alanlarınızın ve JSON özelliklerinizin önüne her zaman "is" koyarsanız işe yarar . Diyelim ki, bu şekilde serileştirmek istediğiniz "etkin" adlı başka bir boole alanınız var. Oluşturulan yöntem "isEnabled ()" olduğundan, yukarıdaki kod daha sonra onu "etkin" yerine "isEnabled" olarak serileştirecektir. Sonuçta sorun, hem "x" hem de "isX" alanları için, Eclipse'in "isX ()" yöntemini oluşturmasıdır; bu nedenle alanla eşleşen bir özellik adı çıkaramazsınız.
David Siegal

Burak cevabındaki @DavidSiegal tabanı Böyle bir durumu desteklemek için aşağıdaki cevabı genişlettim.
edmundpie

4

Kotlin'i ve veri sınıflarını kullanırken:

data class Dto(
    @get:JsonProperty("isSuccess") val isSuccess: Boolean
)

@param:JsonProperty("isSuccess")JSON'u seri durumdan çıkaracaksanız da eklemeniz gerekebilir .


2

Utkarsh'ın cevabına dayanarak ..

Getter adları eksi get / JSON adı olarak kullanılır.

public class Example{
    private String radcliffe; 

    public getHarryPotter(){
        return radcliffe; 
    }
}

{"harryPotter": "whatYouGaveHere"} olarak saklanır


Seriyi kaldırma için, Jackson hem ayarlayıcıyı hem de alan adını kontrol eder. Json String {"word1": "example"} için aşağıdakilerin ikisi de geçerlidir.

public class Example{
    private String word1; 

    public setword2( String pqr){
        this.word1 = pqr; 
    }
}

public class Example2{
    private String word2; 

    public setWord1(String pqr){
        this.word2 = pqr ; 
    }
}

Daha ilginç bir soru, Jackson'ın seriyi kaldırma için hangi siparişi değerlendirdiği. İ serisini denerseniz : { "myName" "WORD1"} ile

public class Example3{
    private String word1;
    private String word2; 

    public setWord1( String parameter){
        this.word2 = parameter ; 
    }
}

Yukarıdaki durumu test etmedim, ancak word1 & word2 değerlerini görmek ilginç olurdu ...

Not: Hangi alanların aynı olması gerektiğini vurgulamak için büyük ölçüde farklı isimler kullandım.


1

bu problem için başka bir yöntem var.

sadece yeni bir alt sınıf tanımlayın PropertyNamingStrategy'yi genişletir ve bunu ObjectMapper örneğine iletir.

İşte bir kod pasajı daha fazla yardımcı olabilir:

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
        @Override
        public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
            String input = defaultName;
            if(method.getName().startsWith("is")){
                input = method.getName();
            }

            //copy from LowerCaseWithUnderscoresStrategy
            if (input == null) return input; // garbage in, garbage out
            int length = input.length();
            StringBuilder result = new StringBuilder(length * 2);
            int resultLength = 0;
            boolean wasPrevTranslated = false;
            for (int i = 0; i < length; i++)
            {
                char c = input.charAt(i);
                if (i > 0 || c != '_') // skip first starting underscore
                {
                    if (Character.isUpperCase(c))
                    {
                        if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
                        {
                            result.append('_');
                            resultLength++;
                        }
                        c = Character.toLowerCase(c);
                        wasPrevTranslated = true;
                    }
                    else
                    {
                        wasPrevTranslated = false;
                    }
                    result.append(c);
                    resultLength++;
                }
            }
            return resultLength > 0 ? result.toString() : input;
        }
    });

1

Bazı özel adlandırma stratejileriyle uğraşmak veya bazı erişimcileri yeniden oluşturmak istemedim.
Ne kadar az kod, o kadar mutluyum.

Bu bizim için hile yaptı:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties({"success", "deleted"}) // <- Prevents serialization duplicates 
public class MyResponse {

    private String id;
    private @JsonProperty("isSuccess") boolean isSuccess; // <- Forces field name
    private @JsonProperty("isDeleted") boolean isDeleted;

}

1

Kabul edilen cevap davam için işe yaramayacak.

Benim durumumda, sınıf bana ait değil. Sorunlu sınıf, 3. parti bağımlılıklardan geliyor, bu yüzden ona sadece @JsonPropertyaçıklama ekleyemiyorum .

Bunu çözmek için yukarıdaki @burak cevabından esinlenerek, PropertyNamingStrategyaşağıdaki gibi bir gelenek oluşturdum :

mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
  @Override
  public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
  {
    if (method.getParameterCount() == 1 &&
            (method.getRawParameterType(0) == Boolean.class || method.getRawParameterType(0) == boolean.class) &&
            method.getName().startsWith("set")) {

      Class<?> containingClass = method.getDeclaringClass();
      String potentialFieldName = "is" + method.getName().substring(3);

      try {
        containingClass.getDeclaredField(potentialFieldName);
        return potentialFieldName;
      } catch (NoSuchFieldException e) {
        // do nothing and fall through
      }
    }

    return super.nameForSetterMethod(config, method, defaultName);
  }

  @Override
  public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
  {
    if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
        && method.getName().startsWith("is")) {

      Class<?> containingClass = method.getDeclaringClass();
      String potentialFieldName = method.getName();

      try {
        containingClass.getDeclaredField(potentialFieldName);
        return potentialFieldName;
      } catch (NoSuchFieldException e) {
        // do nothing and fall through
      }
    }
    return super.nameForGetterMethod(config, method, defaultName);
  }
});

Temel olarak bunun yaptığı şey, serileştirmeden ve seriyi kaldırmadan önce, hedef / kaynak sınıfta sınıfta hangi özellik adının mevcut olduğunu, isEnabledister enabledözellik ister özellik olsun, kontrol etmektir .

Buna dayanarak, eşleyici, var olan özellik adına seri hale getirecek ve serisini kaldıracaktır.


0

İlkel boolean java.lang.Boolean (+ use @JsonPropery) olarak değiştirebilirsiniz

@JsonProperty("isA")
private Boolean isA = false;

public Boolean getA() {
    return this.isA;
}

public void setA(Boolean a) {
    this.isA = a;
}

Benim için mükemmel çalıştı.

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.