Джексон перейменовує примітивне булеве поле, видаляючи 'є'


93

Це може бути дублікат. Але я не можу знайти рішення своєї проблеми.

У мене є клас

public class MyResponse implements Serializable {

    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

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

Геттери та сетери створюються Eclipse.

В іншому класі я встановлюю значення true і записую його як рядок JSON.

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

У JSON ключ надходить як {"success": true}.

Я хочу ключ як isSuccessсам. Чи використовує Джексон метод сеттера під час серіалізації? Як зробити ключ самим іменем поля?


1
якщо ваша назва власності схожа, isSuccessви маєте назвати метод, isIsSuccessя думаю
Йенс,

Я розумію. Я думав, що це краще, SetSuccess оскільки це генерується Eclipse. (Дотримуючись стандарту)
iCode

Відповіді:


119

Це трохи пізня відповідь, але може бути корисною для всіх, хто заходить на цю сторінку.

Просте рішення для зміни імені, для якого Джексон використовуватиме при серіалізації до JSON, це використання анотації @JsonProperty , тому ваш приклад стане:

public class MyResponse implements Serializable {

    private boolean isSuccess;

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

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

Потім це буде серіалізовано в JSON as {"isSuccess":true}, але має ту перевагу, що не потрібно змінювати назву методу отримання.

Зверніть увагу, що в цьому випадку ви також можете написати анотацію, @JsonProperty("isSuccess")оскільки вона містить лише один valueелемент


Цей метод не буде працювати в моєму випадку, оскільки клас не належить мені, оскільки він походить від сторонніх залежностей. Для такого випадку див. Мою відповідь нижче.
edmundpie

4
Я використовую весняне завантаження з jackson, але отримання двох полів одне - це "успіх", а друге - "isSuccess", а коли я використовую не примітивний булев, ніж лише одне поле "isSuccess"
Vishal Singla,

@VishalSingla У мене така сама проблема, це рішення створює два поля у Spring Boot
Арон Фіхтер,

22

Нещодавно я зіткнувся з цим питанням, і ось що я знайшов. Джексон перевірить будь-який клас, який ви йому передасте, на наявність геттерів і сеттерів, і використає ці методи для серіалізації та десеріалізації. Те, що слідує за "get", "is" і "set" у цих методах, буде використано як ключ для поля JSON ("isValid" для getIsValid і setIsValid).

public class JacksonExample {   

    private boolean isValid = false;

    public boolean getIsValid() {
        return isValid;
    }

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

Подібним чином "isSuccess" стане "успіхом", якщо не буде перейменовано на "isIsSuccess" або "getIsSuccess"

Детальніше читайте тут: http://www.citrine.io/blog/2015/5/20/jackson-json-processor


6
isValid не є правильним правилом іменування для логічного типу даних у Java. має бути дійсним та isValid (), setValid ()
vels4j

2
але хіба це не повинно бути саме таким? З'їзд? Якщо він існує, чи можете ви посилатися на посилання Джексона, яке говорить, що воно використовує імена геттерів як поля JSON? Або ви вважаєте, що це поганий вибір дизайну?
Абхінав Вішак

2
Я хотів би, щоб про це було попередження
RyPope

@ vels4j Конвенції про іменування виходять з вікна, коли ви маєте справу з дуже конкретними реалізаціями.
Драгаш

13

Використовуючи обидві анотації нижче, змушує вихідний JSON включати is_xxx:

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

Це найкраща відповідь на це питання.
Дастіневан

1
Це Java? Може, це Котлін?
spottedmahn

5

Ви можете налаштувати ваш 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
Мені подобається, що ви намагаєтеся вирішити це за допомогою конфігурації. Однак це буде працювати лише в тому випадку, якщо ви завжди ставите перед булевими полями та властивостями JSON префікс "є". Скажімо, у вас є ще одне логічне поле з простою назвою "увімкнено", яке ви хочете серіалізувати як таке. Оскільки згенерований метод має значення "isEnabled ()", наведений вище код буде серіалізувати його на "isEnabled", а не просто "увімкнено". Зрештою, проблема полягає в тому, що для обох полів "x" та "isX" Eclipse генерує метод "isX ()"; тож ви не можете зробити висновок про назву властивості, що відповідає полю.
Девід Сігал,

@DavidSiegal на основі відповіді burak Я розширив відповідь нижче, щоб підтримати такий випадок.
edmundpie

4

Коли ви використовуєте Kotlin та класи даних:

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

Можливо, вам доведеться додати, @param:JsonProperty("isSuccess")якщо ви також збираєтеся десериалізувати JSON.


2

Спираючись на відповідь Уткарша ..

Імена одержувачів мінус get / is використовуються як ім'я JSON.

public class Example{
    private String radcliffe; 

    public getHarryPotter(){
        return radcliffe; 
    }
}

зберігається як {"harryPotter": "whateverYouGaveHere"}


Щодо десериалізації, Джексон перевіряє як сетер, так і ім’я поля. Для рядка Json {"word1": "example"} обидва наведені нижче дії є дійсними.

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

Більш цікавим є питання, який наказ Джексон вважає десериалізацією. Якщо я спробую десеріалізувати {"word1": "myName"} за допомогою

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

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

Я не тестував вищезазначений випадок, але було б цікаво побачити значення word1 & word2 ...

Примітка: Я використовував кардинально різні імена, щоб підкреслити, які поля повинні бути однаковими.


1

існує інший метод вирішення цієї проблеми.

просто визначте новий підклас розширює PropertyNamingStrategy і передайте його екземпляру ObjectMapper.

ось фрагмент коду може допомогти більше:

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

Я не хотів возитися з якимись спеціальними стратегіями іменування, ані заново створювати деякі аксесуари.
Чим менше коду, тим я щасливіший.

Це зробило для нас фокус:

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

Прийнята відповідь не підійде для мого випадку.

У моєму випадку клас не належить мені. Проблемний клас походить від сторонніх залежностей, тому я не можу просто додати @JsonPropertyв нього анотацію.

Щоб її вирішити, надихнувшись відповіддю @burak вище, я створив спеціальний приклад PropertyNamingStrategyнаступним чином:

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

В основному, це робиться так, що перед серіалізацією та десеріалізацією він перевіряє в цільовому / вихідному класі, яке ім'я властивості присутнє в класі, чи є воно, isEnabledабо enabledвластивість.

Виходячи з цього, картограф буде серіалізувати та десеріалізувати існуюче ім’я властивості.


0

Ви можете змінити примітивне логічне значення на java.lang.Boolean (+ використання @JsonPropery)

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

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

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

Відмінно працював у мене.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.