Использование внешнего API и преобразование из строки JSON в bean-компонент не инициализирует свойства. Атрибуты объекта NULL. Весенний ботинок, Мейвен

Это ответ, который я получаю от API.

{"получить":"статистика","параметры":{"страна":"румыния"},"ошибки":[],"результаты":1,"ответ":[{"континент":"Европа", «страна»: «Румыния», «население»: 19016885, «случаи»: {«новые»: «+4521», «активные»: 156487, «критические»: 431, «выздоровевшие»: 2606660, «1M_pop»: "148707","всего":2827936},"смертей":{"новых":"+35","1M_pop":"3407","всего":64789},"испытаний":{"1M_pop":" 1149381","всего":21857638},"день":"2022-03-24","время":"2022-03-24T07:30:04+00:00"}]}

@RestController
public class CovidTrackerRestController {
    
    @GetMapping("/hello")
    public String showCovidInformation() {
        
        // connect to a covid database
                        
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://covid-193.p.rapidapi.com/statistics?country=romania"))
                .header("X-RapidAPI-Host", "covid-193.p.rapidapi.com")
                .header("X-RapidAPI-Key", "mykey")
                .method("GET", HttpRequest.BodyPublishers.noBody())
                .build();
        HttpResponse<String> response = null;
        
        try {
            response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
        } catch (IOException | InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
                        
        // get the information
        String responseString = response.body();
        System.out.println(responseString);
        
        Response romaniaData = null;
        
        try {
            romaniaData = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                            .readValue(responseString, Response.class);
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            
        
        // format the information
        System.out.println(romaniaData);
                
        
        // send the information to html page
        return "/tracker";
    }
}

И это мой класс Bean, который помечен @Bean в классе конфигуратора рядом с bean-компонентом RestTemplate. Другие свойства, такие как Cases, Deaths и т. д., настраиваются так же, как класс Response, за исключением того, что они объявляются как @Bean в конфигураторе, потому что, насколько я знаю, как только я объявляю класс @Bean, другие содержащиеся ссылки также автоматически становятся bean-компонентами.

@JsonIgnoreProperties(ignoreUnknown = true)
public class Response {

    @JsonProperty("country")
    private String country;
    
    @JsonProperty("cases")
    private Cases cases;
    
    @JsonProperty("deaths")
    private Deaths deaths;
    
    @JsonProperty("day")
    private String day;
    
    @JsonProperty("time")
    private String time;
    
    @JsonProperty("test")
    private Tests tests;
    
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }

Прежде всего, вы не должны помещать ключи API в вопросы о переполнении стека.

Dan Serb 24.03.2022 09:18

@DanSerb ох, хороший улов! Забыл про ключ.

Art 24.03.2022 09:30

Класс ответа должен быть POJO, зачем вам вводить это с помощью @Autowired? Классы, используемые для хранения сущностей или, в данном случае, ответа, не должны быть bean-компонентами, вы должны создавать экземпляры для каждого вызова API, поэтому он должен быть членом фактического метода, а не класса. Вам не нужно объявлять эти классы как bean-компоненты.

Dan Serb 24.03.2022 09:40

Строка @Autowired осталась с предыдущей попытки, я забыл ее удалить. Я переназначил ссылку romaniaData на нулевое значение ниже и создал экземпляр в блоке try. Я отредактировал вопрос, чтобы удалить 2 боба. Результат, во всяком случае, тот же.

Art 24.03.2022 09:51

Ваш Response — это только часть всего ответа, который вы получаете от API. Он представляет только один элемент в свойстве response (который также является массивом) в json, а не весь json.

Chaosfire 24.03.2022 10:02

Кстати, ваш ключ API все еще можно увидеть в истории сообщений. Думаю лучше будет поменять.

Chaosfire 24.03.2022 10:04

@Chaosfire спасибо, я изменил его и удалил старый. Не могли бы вы посоветовать в ответе, как подойти к настройке? Я впервые пробую такое приложение после прохождения курса Spring Boot, и я не знаю, что делать дальше. В течение последних нескольких дней проводились исследования, но нет соответствующих ресурсов о том, как использовать внешние API, кроме как из файлов CSV.

Art 24.03.2022 10:13
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
7
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваш класс Java должен быть точным представлением полученного json. Назовем это Wrapper:

public class Wrapper {

    @JsonProperty("response")
    private List<Response> responses;

    public List<Response> getResponses() {
        return this.responses;
    }

    public void setResponses(List<Response> responses) {
        this.responses = responses;
    }

    @Override
    public String toString() {
        return "Wrapper{" +
                "responses = " + responses +
                '}';
    }
}

Я опускаю некоторые свойства - get, results и т. д. Похоже, они вам не нужны. Тогда десериализация будет выглядеть так:

Wrapper data = null;
try {
    data = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .readValue("json", Wrapper.class);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
System.out.println(data);

Несколько заметок:

  1. Если имя свойства json совпадает с именем поля в классе, нет необходимости в @JsonProperty
  2. Для тестов аннотация поля должна быть - @JsonProperty("tests"). Собственность tests, а не test

Если вы действительно хотите выбросить остальные данные и вам нужно только свойство response, вам нужно написать собственный десериализатор и работать с деревом json. Вы можете увидеть, как это сделать, например, в мой ответ здесь или это руководство. Таким образом вы можете разобрать ответ json на свой класс, даже если их структуры не совпадают.

ДА это сработало! Большое спасибо, я работал над этим последние 2 дня, не могу поверить, что это действительно сработало! Я вижу, что мне нужно научиться сопоставлять многоуровневые jsons с pojo. Любые рекомендации по ресурсам для этого?

Art 24.03.2022 11:51

Ну и дела @Chaosfire, мы дали очень похожие ответы почти в одно и то же время. +1 тебе от меня

Michael Gantman 24.03.2022 12:10

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

Chaosfire 24.03.2022 12:10

@MichaelGantman Ха-ха, да, иногда такое случается :)

Chaosfire 24.03.2022 12:17

Да, ваш класс должен быть таким:

public class ResponseWrapper {
  public List<Response> response;

  public setResponse(List<Response> response) {
    this.response= response;
  }
  public List<Response> getResponse() {
    return response;
  }
}

А класс Response — это ваш класс, как вы его опубликовали. Ваш класс должен иметь ту же структуру, что и JSON.

Ответ представляет собой массив в json, так работать не будет.

Chaosfire 24.03.2022 11:45

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

Michael Gantman 24.03.2022 11:48

Другие вопросы по теме