Jackson benennt das primitive boolesche Feld um, indem er 'is' entfernt.

93

Dies könnte ein Duplikat sein. Aber ich kann keine Lösung für mein Problem finden.

ich habe ein klasse

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

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

Getter und Setter werden von Eclipse generiert.

In einer anderen Klasse setze ich den Wert auf true und schreibe ihn als JSON-Zeichenfolge.

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

In JSON kommt der Schlüssel als {"success": true}.

Ich möchte den Schlüssel als sich isSuccessselbst. Verwendet Jackson während der Serialisierung die Setter-Methode? Wie mache ich den Schlüssel zum Feldnamen selbst?

iCode
quelle
1
Wenn Ihr Eigenschaftsname liek isSuccessist, muss Ihr Methodenname isIsSuccessich denke
Jens
Ich verstehe. Ich dachte, es ist besser, SetSuccess da es von Eclipse generiert wird. (Nach einem Standard)
iCode

Antworten:

119

Dies ist eine etwas späte Antwort, kann aber für alle anderen nützlich sein, die auf diese Seite kommen.

Eine einfache Lösung zum Ändern des Namens, für den Jackson bei der Serialisierung in JSON verwendet wird, ist die Verwendung der Annotation @JsonProperty. Ihr Beispiel lautet also:

public class MyResponse implements Serializable {

    private boolean isSuccess;

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

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

Dies würde dann in JSON als serialisiert {"isSuccess":true}, hat jedoch den Vorteil, dass Sie den Namen Ihrer Getter-Methode nicht ändern müssen.

Beachten Sie, dass Sie in diesem Fall auch die Anmerkung schreiben können, @JsonProperty("isSuccess")da sie nur das einzelne valueElement enthält

Scott
quelle
Diese Methode funktioniert in meinem Fall nicht, da die Klasse mir nicht gehört, da sie von Abhängigkeiten von Drittanbietern stammt. Für einen solchen Fall siehe meine Antwort unten.
Edmundpie
4
Ich benutze Spring Boot mit Jackson, aber zwei Felder zu bekommen ist "Erfolg" und das andere ist "isSuccess" und wenn ich nicht primitiven Booleschen Wert verwende, dann ist nur ein Feld "isSuccess"
Vishal Singla
@ VishalSingla Ich habe genau das gleiche Problem, diese Lösung erzeugt zwei Felder in Spring Boot
Aron Fiechter
22

Ich bin kürzlich auf dieses Problem gestoßen und habe es gefunden. Jackson überprüft jede Klasse, die Sie an sie übergeben, auf Getter und Setter und verwendet diese Methoden für die Serialisierung und Deserialisierung. Was in diesen Methoden auf "get", "is" und "set" folgt, wird als Schlüssel für das JSON-Feld verwendet ("isValid" für getIsValid und setIsValid).

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

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

In ähnlicher Weise wird "isSuccess" zu "Erfolg", sofern nicht in "isIsSuccess" oder "getIsSuccess" umbenannt wird.

Lesen Sie hier mehr: http://www.citrine.io/blog/2015/5/20/jackson-json-processor

Utkarsha Padhye
quelle
6
isValid ist keine richtige Namenskonvention für den booleschen Datentyp in Java. sollte gültig sein und isValid (), setValid ()
vels4j
2
aber soll es nicht genau das sein? Eine Konvention ? Wenn es existiert, könnten Sie einen Link zu einer Jackson-Referenz erstellen, die besagt, dass Getter-Namen als JSON-Felder verwendet werden? Oder denkst du, es ist eine schlechte Wahl für das Design?
Abhinav Vishak
2
Ich wünschte, es gäbe eine Warnung dafür
RyPope
@ vels4j Namenskonventionen gehen aus dem Fenster, wenn Sie mit hochspezifischen Implementierungen arbeiten.
Dragas
13

Wenn Sie beide unten stehenden Anmerkungen verwenden, wird der Ausgabe-JSON gezwungen, Folgendes einzuschließen is_xxx:

@get:JsonProperty("is_something")
@param:JsonProperty("is_something")
Fabio
quelle
Dies ist die beste Antwort auf diese Frage.
Dustinevan
1
Ist das Java? Vielleicht ist es Kotlin?
Spottedmahn
5

Sie können Ihre ObjectMapperwie folgt konfigurieren :

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);
            }
        });
Burak Emre
quelle
1
Ich finde es gut, dass Sie versuchen, dies über die Konfiguration zu lösen. Dies wird jedoch nur funktionieren , wenn Sie immer Präfix Ihrer boolean Felder und JSON - Eigenschaften mit „ist“. Angenommen, Sie haben ein anderes boolesches Feld mit dem Namen "enabled", das Sie als solches serialisieren möchten. Da die generierte Methode "isEnabled ()" ist, serialisiert der obige Code sie dann in "isEnabled" anstatt nur in "enabled". Letztendlich besteht das Problem darin, dass Eclipse für beide Felder "x" und "isX" die Methode "isX ()" generiert. Sie können also keinen Eigenschaftsnamen ableiten, der mit dem Feld übereinstimmt.
David Siegal
@ DavidSiegal Basis auf Burak-Antwort Ich habe die Antwort unten erweitert, um einen solchen Fall zu unterstützen.
Edmundpie
4

Wenn Sie Kotlin und Datenklassen verwenden:

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

Möglicherweise müssen Sie hinzufügen, @param:JsonProperty("isSuccess")wenn Sie JSON ebenfalls deserialisieren möchten.

Eirik Fosse
quelle
2

Aufbauend auf Utkarshs Antwort.

Getter-Namen minus get / is wird als JSON-Name verwendet.

public class Example{
    private String radcliffe; 

    public getHarryPotter(){
        return radcliffe; 
    }
}

wird gespeichert als {"harryPotter": "WhateverYouGaveHere"}


Bei der Deserialisierung prüft Jackson sowohl den Setter als auch den Feldnamen. Für den Json-String {"word1": "example"} gelten beide folgenden Punkte .

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

Eine interessantere Frage ist, welche Reihenfolge Jackson für die Deserialisierung in Betracht zieht. Wenn ich versuche, {"word1": "myName"} mit zu deserialisieren

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

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

Ich habe den obigen Fall nicht getestet, aber es wäre interessant, die Werte von Wort1 und Wort2 zu sehen ...

Hinweis: Ich habe drastisch unterschiedliche Namen verwendet, um hervorzuheben, welche Felder identisch sein müssen.

Abhinav Vishak
quelle
1

Es gibt eine andere Methode für dieses Problem.

Definieren Sie einfach eine neue Unterklasse, erweitern Sie PropertyNamingStrategy und übergeben Sie sie an die ObjectMapper-Instanz.

Hier ist ein Code-Snippet kann mehr helfen:

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;
        }
    });
Eric
quelle
1

Ich wollte mich nicht mit benutzerdefinierten Benennungsstrategien herumschlagen oder einige Accessoren neu erstellen.
Je weniger Code, desto glücklicher bin ich.

Das hat uns geholfen:

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;

}
Adrien
quelle
1

Die akzeptierte Antwort funktioniert in meinem Fall nicht.

In meinem Fall gehört die Klasse nicht mir. Die problematische Klasse stammt aus Abhängigkeiten von Drittanbietern, daher kann ich nicht einfach @JsonPropertyAnmerkungen hinzufügen .

Um das Problem zu lösen, habe ich, inspiriert von der obigen Antwort von @burak, einen benutzerdefinierten Code PropertyNamingStrategywie folgt erstellt:

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

Grundsätzlich wird vor dem Serialisieren und Deserialisieren in der Ziel- / Quellklasse überprüft, welcher Eigenschaftsname in der Klasse vorhanden ist, ob es sich um einen handelt isEnabledoder nichtenabled Eigenschaft .

Basierend darauf serialisiert und deserialisiert der Mapper den vorhandenen Eigenschaftsnamen.

Edmundpie
quelle
0

Sie können den primitiven Booleschen Wert in java.lang.Boolean ändern (+ verwenden @JsonPropery)

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

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

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

Hat hervorragend für mich funktioniert.

Reynard
quelle