Jackson ObjectMapper возвращает значение NULL, когда для переменной Object установлено значение Private - PullRequest
2 голосов
/ 26 сентября 2019

Мне дан этот экранированный JSON

"{\"UniqueId\":[],\"CustomerOffers\":{},\"Success\":false,\"ErrorMessages\":[\"Test Message\"],\"ErrorType\":\"GeneralError\"}"

, и мне нужно преобразовать его в объект Java с помощью Джексона.

// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8'

Я создал класс:

public class Data {

    private List<UUID> UniqueId;
    private Map<Integer, List<Integer>> CustomerOffers;
    private Boolean Success;
    private List<String> ErrorMessages;
    private String ErrorType;

    // getter, setters
}

Затем я создал метод для его преобразования

public class Deserializing {

    public void processing(String input) {

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);



        String jsonInString = "\"{\"UniqueId\":[],\"CustomerOffers\":{},\"Success\":false,\"ErrorMessages\":[\"Test Message\"],\"ErrorType\":\"GeneralError\"}\"";
        String newJSON = org.apache.commons.lang3.StringEscapeUtils.unescapeJava(jsonInString);
        newJSON= newJSON.substring(1, jsonInString.length()-1);


            try {
            // JSON string to Java object
            Data data = mapper.readValue(newJSON, Data.class); 
            System.out.println(data);

            System.out.println("Get Success "+ data.getSuccess());  // return "false" if Data object is public ; null if private
            System.out.println("Get UniqueID " + data.getUniqueId()); // return [] if Data object is public ; null if private

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

Какие бы переменные в классе данных не были установлены как открытыетогда я получу the corresponding value когда я позвоню получателям.Какие бы переменные в классе данных не были определены как private, я получу null, когда вызову getter.

Getter and Setters всегда общедоступны.

Мне интересно, почему ObjectMapper не может сопоставить объект, если он установлен в private?Я мог бы сделать это публичным, но это не лучшая практика.

Ответы [ 3 ]

2 голосов
/ 26 сентября 2019

Проблема в том, что Джексон всегда будет предполагать, что setSuccess() & getSuccess() будет использоваться для поля success, а не Success.Имена полей JSON, начинающиеся с заглавных букв, должны поддерживаться @JsonProperty.В Java существует соглашение, согласно которому члены класса всегда начинаются со строчных букв, вы можете понять, что с помощью этой аннотации.

Когда вы делаете поля private, вы заставляете Джексона использовать сеттеры, и вышеуказанный конфликт делает невозможнымчтобы правильно десериализовать объект Data.

Решение - это сделать;

public class Data {

    @JsonProperty("UniqueId")
    private List<UUID> uniqueId;
    @JsonProperty("CustomerOffers")
    private Map<Integer, List<Integer>> customerOffers;
    @JsonProperty("Success")
    private Boolean success;
    @JsonProperty("ErrorMessages")
    private List<String> errorMessages;
    @JsonProperty("ErrorType")
    private String errorType;

    // getter&setters
}

Тогда вы увидите, как значения десериализованы правильно в объект Java;

Get success false
Get uniqueID []
1 голос
/ 26 сентября 2019

В спецификации JavaBeans (универсальное руководство по структурированию свойств класса Java) свойства определяются как начинающиеся со строчной буквы (errorMessages);аксессоры, потому что они имеют префикс, пишут его с заглавной буквы (getErrorMessages).Джексон использует имена свойств по умолчанию, поэтому, когда у вас есть метод getErrorMessages, он ищет свойство JSON с ключом errorMessages.

. Лучший способ - изменить ваш JSON;Я видел стили errorMessages и error_messages, но никогда ErrorMessages.Если вы не можете этого сделать, вы можете применить @JsonProperty к вашим свойствам, чтобы указать Джексону использовать другое имя в JSON.

1 голос
/ 26 сентября 2019

По умолчанию Джексон будет пытаться сериализовать открытые поля только для класса Data (или любого другого класса, который вы пытаетесь сериализовать / десериализовать).Однако вы можете настроить ObjectMapper, чтобы разрешить сериализацию всех полей независимо от их видимости:

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
Data data = mapper.readValue(newJSON, Data.class);

См. здесь и здесь для получения дополнительной информации.

...