Jackson renomeia o campo booleano primitivo removendo 'é'


97

Pode ser uma duplicata. Mas não consigo encontrar uma solução para o meu problema.

Eu tenho uma aula

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

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

Getters e setters são gerados pelo Eclipse.

Em outra classe, defino o valor como true e o escrevo como uma string JSON.

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

Em JSON, a chave está chegando {"success": true}.

Eu quero a chave como isSuccessela mesma. Jackson está usando o método setter durante a serialização? Como transformar a chave no próprio nome do campo?


1
se o nome da sua propriedade for como isSuccesso nome do seu método deve ser isIsSuccesseu acho
— Jens

Compreendo. Achei melhor SetSuccess já que foi gerado pelo Eclipse. (Seguindo um padrão)
— iCode

Respostas:


121

Esta é uma resposta um pouco tardia, mas pode ser útil para qualquer pessoa que visite esta página.

Uma solução simples para alterar o nome que Jackson usará ao serializar para JSON é usar a anotação @JsonProperty , portanto, seu exemplo seria:

public class MyResponse implements Serializable {

    private boolean isSuccess;

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

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

Isso seria então serializado para JSON como {"isSuccess":true}, mas tem a vantagem de não precisar modificar o nome do método getter.

Observe que, neste caso, você também pode escrever a anotação, @JsonProperty("isSuccess")pois ela só tem um único valueelemento


Este método não funcionará no meu caso, uma vez que a classe não é minha, visto que vem de dependências de terceiros. Para esse caso, veja minha resposta abaixo.
— edmundpie

5
Estou usando spring boot com jackson, mas obtendo dois campos, um é "sucesso" e o outro é "isSuccess" e quando eu uso um booleano não primitivo, apenas um campo "isSuccess"
— Vishal Singla

@VishalSingla Tenho exatamente o mesmo problema, esta solução produz dois campos no Spring Boot
— Aron Fiechter

serialização com GSON funciona bem.
— Nayeem

23

Recentemente, encontrei esse problema e foi isso que descobri. Jackson inspecionará qualquer classe que você passar para ele em busca de getters e setters e usará esses métodos para serialização e desserialização. O que segue "get", "is" e "set" nesses métodos será usado como a chave para o campo JSON ("isValid" para getIsValid e setIsValid).

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

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

Da mesma forma, "isSuccess" se tornará "sucesso", a menos que seja renomeado para "isIsSuccess" ou "getIsSuccess"

Leia mais aqui: http://www.citrine.io/blog/2015/5/20/jackson-json-processor


6
isValid não é a convenção de nomenclatura correta para o tipo de dados booleano em java. deve ser válido e isValid (), setValid ()
— vels4j 01 de

2
mas não deveria ser exatamente isso? Uma convenção? Se ele existir, você poderia vincular a referência de Jackson que diz que ele usa nomes getter como os campos JSON? Ou você acha que é uma má escolha de design?
— Abhinav Vishak

2
Gostaria que houvesse um aviso para isso
— RyPope

@ vels4j As convenções de nomenclatura saem pela janela quando você está lidando com implementações altamente específicas.
— Dragas,

13

Usando as duas anotações abaixo, força o JSON de saída a incluir is_xxx:

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

Esta é a melhor resposta para esta pergunta.
— dustinevan de

2
Isso é Java? Talvez seja Kotlin?
— spottedmahn

7

Você pode configurar o seu da ObjectMapperseguinte maneira:

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
Gosto que você esteja tentando resolver isso via configuração. No entanto, isso só funcionará se você sempre prefixar seus campos booleanos e propriedades JSON com "is". Digamos que você tenha outro campo booleano simplesmente denominado "ativado" que deseja serializar como tal. Como o método gerado é "isEnabled ()", o código acima o serializará para "isEnabled" em vez de apenas "enabled". Em última análise, o problema é que para ambos os campos "x" e "isX", o Eclipse gera o método "isX ()"; portanto, você não pode inferir um nome de propriedade que corresponda ao campo.
— David Siegal

@DavidSiegal base on burak answer Estendi a resposta abaixo para apoiar esse caso.
— edmundpie

5

Quando você estiver usando Kotlin e classes de dados:

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

Talvez seja necessário adicionar @param:JsonProperty("isSuccess")se você for desserializar JSON também.


2

Com base na resposta de Utkarsh ..

Nomes de getter menos get / is é usado como o nome JSON.

public class Example{
    private String radcliffe; 

    public getHarryPotter(){
        return radcliffe; 
    }
}

é armazenado como {"harryPotter": "qualquer que seja YouGaveHere"}


Para desserialização, Jackson verifica o setter e o nome do campo. Para a String Json {"palavra1": "exemplo"} , ambas as opções abaixo são válidas.

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

Uma questão mais interessante é qual ordem Jackson considera para desserialização. Se eu tentar desserializar {"word1": "myName"} com

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

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

Não testei o caso acima, mas seria interessante ver os valores de palavra1 e palavra2 ...

Observação: usei nomes drasticamente diferentes para enfatizar quais campos devem ser iguais.


2

Eu não queria mexer com algumas estratégias de nomenclatura personalizadas, nem recriar alguns acessadores.
Quanto menos código, mais feliz eu fico.

Isso funcionou para nós:

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

existe outro método para este problema.

basta definir uma nova subclasse que estende PropertyNamingStrategy e passá-la para a instância ObjectMapper.

aqui está um snippet de código que pode ser mais útil:

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

A resposta aceita não funcionará para o meu caso.

No meu caso, a classe não é minha. A classe problemática vem de dependências de terceiros, então não posso simplesmente adicionar @JsonPropertyanotações a ela.

Para resolvê-lo, inspirado na resposta de @burak acima, criei um custom PropertyNamingStrategycomo segue:

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

Basicamente, o que isso faz é, antes de serializar e desserializar, verificar na classe de destino / origem qual nome de propriedade está presente na classe, se é isEnabledou enabledpropriedade.

Com base nisso, o mapeador será serializado e desserializado para o nome da propriedade existente.


0

Você pode alterar o booleano primitivo para java.lang.Boolean (+ usar @JsonPropery)

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

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

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

Funcionou excelente para mim.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.