webentwicklung-frage-antwort-db.com.de

Warum erkennt Jackson 2 den ersten Großbuchstaben nicht, wenn das führende Word von Kamel nur aus einem einzigen Buchstaben besteht?

Ich verwende Spring 4 MVC mit Jackson 2 für meinen Service. Für eine der Operationen habe ich ein Anforderungsobjekt, das über ein Attribut verfügt, in dem das führende Word des Kamels nur einen Buchstaben lang ist:

private String aLogId;

Diese Klasse hat die entsprechend benannten Getter und Setter:

public String getALogId() { return aLogId; }
public void setALogId(String aLogId) { this.aLogId = aLogId; }

Wenn ich jedoch versuche, eine Anforderung mit der entsprechenden JSON-Eigenschaft an diesen Dienst zu senden:

{"aLogId":"This is a log id"}

Ich bekomme eine 500-Antwort vom Spring-Framework, die besagt, dass das Feld nicht erkannt wird und meine Controller-Klasse nie aufgerufen wird:

JSON konnte nicht gelesen werden: Nicht erkanntes Feld "aLogId" (Klasse

Wenn ich jedoch das "L" in Kleinbuchstaben umwandle, wird die Anforderung wie erwartet deserialisiert und meine Controller-Klasse wird getroffen:

{"alogId":"This is a log id"}

Warum erwartet Jackson, dass das "L" Kleinbuchstaben ist, wenn es offensichtlich das zweite Wort in der Kamelfallkonvention für das Attribut ist und in Großbuchstaben vorgesehen ist? Liegt es daran, dass das erste Wort nur einen einzigen Buchstaben enthält?

Es gibt andere Attribute im Anforderungsobjekt, bei denen das erste Word aus mehr als einem Buchstaben besteht und den Attributen nicht das gleiche Problem mit dem Konflikt zugeordnet wird.

16
Hazok

Das Problem, das Sie sehen, ist auf die Tatsache zurückzuführen, dass Jackson Java Bean-Namenskonventionen verwendet, um die Json-Eigenschaften in einer Java-Klasse herauszufinden. 

Hier ist eine Referenz des spezifischen Problems, das Sie sehen. Es wird empfohlen, keinen der ersten beiden Buchstaben in Ihrem Feld groß zu schreiben. Wenn Sie eine IDE wie IntelliJ oder Eclipse verwenden und die IDE die Setter für Sie generieren lassen, werden Sie dasselbe "Verhalten" feststellen, und Sie erhalten die folgenden Methoden:

public void setaLogId(String aLogId) {
    this.aLogId = aLogId;
}

public String getaLogId() {
    return aLogId;
}

Wenn Sie also das "L" in Kleinbuchstaben ändern, konnte Jackson das Feld ermitteln, das Sie zuordnen wollten.

Trotzdem haben Sie immer noch die Alternative, den Feldnamen "aLogId" zu verwenden, damit Jackson nur funktionieren kann, wenn Sie die @JsonProperty-Anmerkung mit der aLogId verwenden. 

@JsonProperty("aLogId")
private String aLogId;

Der folgende Testcode soll zeigen, wie dies funktioniert:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Test {

    @JsonProperty("aLogId")
    private String aLogId;

    public void setaLogId(String aLogId) {
        this.aLogId = aLogId;
    }

    public String getaLogId() {
        return aLogId;
    }

    public static void main(String[] args) {

        ObjectMapper objectMapper = new ObjectMapper();

        Test test = new Test();

        test.setaLogId("anId");

        try {
            System.out.println("Serialization test: " + objectMapper.writeValueAsString(test));


            String json = "{\"aLogId\":\"anotherId\"}";

            Test anotherTest = objectMapper.readValue(json, Test.class);

            System.out.println("Deserialization test: " +anotherTest.getaLogId());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

Die Ausgabe des Tests ist:

Serialization test: {"aLogId":"anId"}

Deserialization test: anotherId

26
jbarrueta

Ich verstehe, dass Jackson standardmäßig seine eigene Namenskonvention verwendet, die der Java Bean-Namenskonvention sehr nahe kommt, jedoch nicht genau dieselbe ist. Die MapperFeature-Option MapperFeature.USE_STD_BEAN_NAMING wurde in Jackson 2.5.0 hinzugefügt, um Jackson mitzuteilen, dass er die Java Bean-Namenskonvention verwenden soll - siehe Jackson Ausgabe 653 . Aus Gründen der Abwärtskompatibilität ist der Standardwert für MapperFeature.USE_STD_BEAN_NAMING "false".

2
Geoff Alexander

Das hat für mich funktioniert; @JsonProperty-Annotation zu Gettern!

import com.fasterxml.jackson.annotation.JsonProperty;

public class PaytmRequestJson {
    private String ORDERID;
    private String MID;
    private String CHECKSUMHASH;

    @JsonProperty("ORDERID")
    public String getORDERID() {
        return ORDERID;
    }

    public void setORDERID(String ORDERID) {
        this.ORDERID = ORDERID;
    }

    @JsonProperty("MID")
    public String getMID() {
        return MID;
    }

    public void setMID(String MID) {
        this.MID = MID;
    }

    @JsonProperty("CHECKSUMHASH")
    public String getCHECKSUMHASH() {
        return CHECKSUMHASH;
    }

    public void setCHECKSUMHASH(String CHECKSUMHASH) {
        this.CHECKSUMHASH = CHECKSUMHASH;
    }
}
1
rupanjan

@JsonProperty, wie von der aktuellen Antwort vorgeschlagen, hat den Nachteil, dass Sie ihn für jede einzelne Eigenschaft wiederholen müssen und dass er invasiv ist (Sie müssen die zugeordnete Klasse ändern).

Ein allgemeinerer Ansatz ist die Bereitstellung einer benutzerdefinierten Eigenschaftsnamensstrategie:

Java :

public class CobraCaseX extends PropertyNamingStrategy.PropertyNamingStrategyBase {
    private static final Pattern REGEX = Pattern.compile("[A-Z]");

    @Override
    public String translate(String input) {
        if (input == null)
            return input; // garbage in, garbage out

        if (!input.isEmpty() && Character.isUpperCase(input.charAt(0)))
            input = input.substring(0, 1).toLowerCase() + input.substring(1);

        return REGEX.matcher(input).replaceAll("_$0").toLowerCase();
    }
}

Kotlin:

class CustomSnakeCase : PropertyNamingStrategy.PropertyNamingStrategyBase() {
    private companion object {
        val REGEX = Regex("[A-Z]")
    }

    override fun translate(input: String?) =
        input?.decapitalize()?.replace(REGEX, "_$0")?.toLowerCase()
}

Verwendungszweck:

new ObjectMapper()
    .setPropertyNamingStrategy(new CustomSnakeCase())
    .enable(MapperFeature.USE_STD_BEAN_NAMING)

Hinweis: Bei der obigen Implementierung wird davon ausgegangen, dass die Eingabe camelCase ist (kein Anfang in Großbuchstaben). USE_STD_BEAN_NAMING wird benötigt, um einzeilige Präfixe wie aField konsistent zu behandeln. 

Die Implementierung bietet die folgende Zuordnung, die Sie möglicherweise an Ihre Bedürfnisse anpassen können:

camelCase      snake_case
----------------------------
simple         simple
a              a
sepaRated      sepa_rated
iOException    i_o_exception
xOffset        x_offset
theWWW         the_w_w_w
sepaRated32    sepa_rated32
sepa32Rated    sepa32_rated
1
TheOperator