Как разобрать json при загрузке Spring и записать в репозиторий

#java #json #spring #spring-boot

#java #json #spring #spring-загрузка

Вопрос:

Я пытаюсь проанализировать ответ JSON API исторических данных временных рядов с потенциально тысячами строк. Ответ в следующем формате:

 {
    "name": "AAPL",
    "history": {
        "2019-03-05": {
            "open": "175.94",
            "close": "175.53",
            "high": "176.00",
            "low": "174.54",
            "volume": "19163899"
        },
        "2019-03-04": {
            "open": "175.69",
            "close": "175.85",
            "high": "177.75",
            "low": "173.97",
            "volume": "27436203"
        }
    }
} 
  

Я хотел бы написать ответ в репозиторий Spring. У меня есть простой код для этого, и ниже показан раздел:

 RestTemplate restTemplate = new RestTemplate();
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = (JsonObject) jsonParser.parse(result);
JsonElement jsonElement = jsonObject.get("history");
Set<Map.Entry<String, JsonElement>> entrySet = jsonElement.getAsJsonObject().entrySet();

for (Map.Entry<String, JsonElement> entry : entrySet) {
    StockHistory stockHistory = new StockHistory();
    stockHistory.setSymbol(stk);
    // .... Other code
}
  

Я устанавливаю свойства объекта в соответствии с ответом JSON, добавляю объект в список и, наконец, сохраняю список в репозитории. Этот процесс происходит очень медленно, предположительно, потому, что я создаю новый StockHistory объект для каждого элемента в возвращаемом файле JSON. Мне было интересно, есть ли лучший способ сделать это.

Комментарии:

1. Не могли бы вы, пожалуйста, проверить обновленный ответ и сообщить мне, если это поможет? Спасибо. 🙂

Ответ №1:

Поскольку вы не можете изменить структуру JSON. Я хотел бы добавить следующий код, который может анализировать JSON, предоставленный вами в простом классе под названием Repo . Для того, чтобы сделать это, вам нужно добавить библиотеку отсюда, которую я использовал.

Теперь вам нужно добавить в свой код следующие классы.

 public class Repo {
    public String name;
    public ArrayList<History> histories;

    public Repo() {
        histories = new ArrayList<History>();
    }
}

public class History {
    public String date;
    public HistoryElements elements;
}

public class HistoryElements {
    public String volume;
    public String high;
    public String low;
    public String close;
    public String open;
}
  

Следовательно, я написал RepoParser и протестировал его с вашей строкой JSON, и он разбивает JSON на классы.

 import com.oracle.javafx.jmx.json.JSONException;
import org.json.JSONObject;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;

public class RepoParser {
    public static Repo parseRepo(String jsonString) throws ParseException, JSONException {

        JSONObject jsonObject = new JSONObject(jsonString);
        Iterator<?> iterator = jsonObject.keys();
        Repo repo = new Repo();

        while (iterator.hasNext()) {
            String obj = iterator.next().toString();

            if (obj.equals("name")) repo.name = obj;
            else repo.histories = parseHistory((JSONObject) jsonObject.get(obj));
        }

        return repo;
    }

    public static ArrayList<History> parseHistory(JSONObject jsonObject) throws ParseException, JSONException {
        Iterator<?> iterator = jsonObject.keys();
        ArrayList<History> historyList = new ArrayList<>();

        while (iterator.hasNext()) {
            String obj = iterator.next().toString();
            History history = new History();
            history.date = obj;
            history.elements = parseHistoryElement((JSONObject) jsonObject.get(obj));

            historyList.add(history);
        }

        return historyList;
    }

    public static HistoryElements parseHistoryElement(JSONObject jsonObject) throws ParseException, JSONException {
        Iterator<?> iterator = jsonObject.keys();
        HistoryElements historyElements = new HistoryElements();

        while (iterator.hasNext()) {
            String obj = iterator.next().toString();

            if (obj.equals("open")) historyElements.open = jsonObject.getString("open");
            if (obj.equals("close")) historyElements.close = jsonObject.getString("close");
            if (obj.equals("high")) historyElements.high = jsonObject.getString("high");
            if (obj.equals("low")) historyElements.low = jsonObject.getString("low");
            if (obj.equals("volume")) historyElements.volume = jsonObject.getString("volume");
        }

        return historyElements;
    }
}
  

Просто используйте RepoParser класс, подобный следующему.

 try {
    Repo repo = RepoParser.parseRepo(jsonString);
} catch (Exception e) {
    e.printStackTrace();
}
  

Я также создал Gist для удобства.

Обновить

Вы могли бы рассмотреть возможность добавления всех Repo в список, а затем сохранить их все в своей базе данных сразу, используя save метод репозитория.

Следовательно, код должен быть примерно следующим.

 try {
    while(there is no repo left for parsing) {
        Repo repo = RepoParser.parseRepo(jsonString);
        repoList.add(repo)
    }

    yourRepository.save(repoList); // Save all values at once

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

Надеюсь, это поможет!

Комментарии:

1. Я не уверен, что понимаю предлагаемую структуру классов. Даты в ответе json ежедневно возвращаются на 20 лет назад. Пожалуйста, объясните.

2. Я обновил свой ответ. Не могли бы вы, пожалуйста, разобраться в этом?

3. У меня нет контроля над файлом Json. Я использую REST API от интернет-провайдера данных.

4. Я не хочу быть придирчивым, но это public class 2019-03-04 недопустимое наименование класса в Java.

5. @makasu, я обновил свой ответ и могу разобрать вашу строку JSON. Не могли бы вы, пожалуйста, попробовать добавить в свой код библиотеку, которую я предложил и на которую предоставил ссылку? И затем, вам нужно попробовать мой анализатор после добавления классов, которые я написал выше. Пожалуйста, дайте мне знать, если возникнет проблема. Я протестировал код на своей стороне, и он работает отлично. Спасибо 🙂

Ответ №2:

После некоторого исследования я обнаружил, что проблема была в режиме гибернации. Насколько я понимаю, полезной особенностью hibernate является то, что он кэширует объекты, но это вызывает проблему, когда создается большое количество объектов для вставки. Проблема может быть решена путем пакетной обработки с использованием spring.jpa.properties.свойство hibernate.jdbc.batch_size и использование генератора последовательностей в классе entity. Теперь сохранение списка из многих тысяч строк происходит намного быстрее.