Подтвердить что ты не робот

Почему Джексон 2 не признает первую заглавную букву, если ведущее слово верблюда - это всего лишь одна буква?

Я использую Spring 4 MVC с Jackson 2 для моей службы. Для одной из операций у меня есть объект запроса, у которого есть атрибут, где ведущее слово верблюда это только одна буква в длину:

private String aLogId;

Этот класс имеет соответственно названные геттеры и сеттеры:

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

Однако, когда я пытаюсь отправить запрос этой службе с использованием соответствующего свойства JSON:

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

Я получаю ответ 500 из структуры Spring, говорящий, что поле не распознано, и мой класс контроллера никогда не вызывается:

Не удалось прочитать JSON: нераспознанное поле "aLogId" (класс

Однако, когда я меняю "L" на нижний регистр, запрос десериализуется, как ожидалось, и мой класс контроллера попадает:

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

Почему Джексон ожидает, что "L" будет нижним регистром, когда он, очевидно, является вторым словом в условном случае для верблюда для атрибута и предназначен для того, чтобы быть в верхнем регистре? Это потому, что первое слово длится только одна буква?

В объекте запроса есть другие атрибуты, где первое слово содержит более одной буквы, а те, которые приписываются, не сталкиваются с этой же проблемой с несоответствием в случае.

4b9b3361

Ответ 1

Проблема, которую вы видите, связана с тем, что Джексон использует соглашения об именах Java Bean, чтобы выяснить свойства Json в классе Java.

Вот ссылка на конкретную проблему, которую вы видите. Рекомендация не состоит в том, чтобы использовать ни одну из первых двух букв в вашем поле. Если вы используете IDE, например IntelliJ или eclipse, и позволяете IDE генерировать для вас сеттеры, вы заметите, что происходит то же "поведение", что в итоге приведет к следующим методам:

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

public String getaLogId() {
    return aLogId;
}

Следовательно, когда вы изменили букву "L" на нижний регистр, Джексон смог определить поле, которое вы хотите отобразить.

Сказав вышесказанное, у вас все еще есть возможность использовать имя поля "aLogId" и заставить Джексона работать, все, что вам нужно сделать, это использовать аннотацию @JsonProperty с aLogId в ней.

@JsonProperty("aLogId")
private String aLogId;

Следующий тестовый код показывает, как это будет работать:

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

    }
}

Результат теста:

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

Deserialization test: anotherId

Ответ 2

Я понимаю, что Джексон по умолчанию использует свое собственное соглашение об именах, которое очень близко, но не совсем то же самое, к соглашению об именах Java Bean. Параметр MapperFeature, MapperFeature.USE_STD_BEAN_NAMING, был добавлен в Jackson 2.5.0, чтобы сообщить Jackson использовать соглашение об именах Java Bean - см. Jackson Issue 653. Для обратной совместимости значение по умолчанию для MapperFeature.USE_STD_BEAN_NAMING является ложным.

Ответ 3

@JsonProperty, как предлагается в текущем ответе, имеет недостаток, заключающийся в том, что вам нужно повторять его для каждого отдельного свойства и делать его инвазивным (вам нужно изменить отображаемый класс).

Более общий подход заключается в предоставлении собственной стратегии именования свойств:

Java:

public class CustomSnakeCase 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();
    }
}

Котлин:

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

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

Использование:

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

Примечание: Реализация, которую я предоставляю выше, предполагает, что вводом является camelCase (без начала в верхнем регистре). USE_STD_BEAN_NAMING необходим для обработки односимвольных префиксов, таких как aField.

Реализация обеспечивает следующее отображение, вы можете настроить его в соответствии со своими потребностями:

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

Ответ 4

Это сработало для меня; @JsonProperty аннотации на геттеры!

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