FlaskアプリのJSONレスポンスをJavaオブジェクトに変換する方法について解説します。
Javaデータクラスのフィールド名がJSONのプロパティ名に一致ししていればGsonライブラリがJSON文字列を対応するJavaオブジェクトに変換してくれます。
java-health-care-example/
├── com
│ └── examples
│ └── android
│ └── healthcare
│ ├── Constants.java (*) 入出力先定義
│ ├── ParseResponseDataJson.java (*) データ取得時レスポンス(JSON)Java変換メインクラス
│ ├── ParseResponseWarningJson.java (*) ウォーニングレスポンス(JSON)Java変換メインクラス
│ ├── data
│ │ ├── GetCurrentDataResult.java (*) データ取得時レスポンス結果クラス
│ │ └── ResponseWarningStatus.java (*) ウォーニングレスポンスクラス
│ └── util
│ └── FileUtil.java (*) ファイル入出力クラス
└── lib
└── gson-2.9.0.jar (*) Mavenリポジトリからダウンロードしlibディレクトリに配置
Healthcare/
└── healthcare
├── __init__.py
└── views
├── __init__.py
└── app_main.py (*) アプリメイン (リクエスト処理)
package com.examples.android.healthcare.data;
public class GetCurrentDataResult {
private final RegisterData data;
private final ResponseStatus status;
public GetCurrentDataResult(RegisterData data, ResponseStatus status) {
this.data = data;
this.status = status;
}
public RegisterData getData() { // JSONのプロパティ名"data"で登録データオブジェクトを格納
return data;
}
public ResponseStatus getStatus() { // JSONのプロパティ名"status"でステータスオブジェクトを格納
return status;
}
//...省略...
}
package com.examples.android.healthcare.data;
public class ResponseWarningStatus {
private final ResponseStatus status;
public ResponseWarningStatus(ResponseStatus status) {
this.status = status;
}
public ResponseStatus getStatus() { // JSONのプロパティ名"status"でステータスオブジェクトを格納
return status;
}
//...省略...
}
{
"data": {
"emailAddress": "user1@examples.com",
"healthcareData": {
"bloodPressure": {
"eveningMax": 132,
"eveningMeasurementTime": "22:55",
"eveningMin": 82,
"eveningPulseRate": 61,
"morningMax": 119,
"morningMeasurementTime": "06:40",
"morningMin": 70,
"morningPulseRate": 69
},
"bodyTemperature": {
"measurementTime": null,
"temperature": null
},
"nocturiaFactors": {
"conditionMemo": "ちょっと風邪気味",
"hasAlcohol": false,
"hasCoffee": true,
"hasDiuretic": false,
"hasNutritionDrink": false,
"hasSportsDrink": false,
"hasTea": false,
"midnightToiletVisits": 0,
"takeBathing": false,
"takeMedicine": false
},
"sleepManagement": {
"deepSleepingTime": null,
"sleepScore": 83,
"sleepingTime": "06:40",
"wakeupTime": "05:30"
},
"walkingCount": {
"counts": 7870
}
},
"measurementDay": "2023-03-01",
"weatherData": {
"weatherCondition": {
"condition": "曇り"
}
}
},
"status": {
"code": 0,
"message": "OK"
}
}
package com.examples.android.healthcare;
import com.examples.android.healthcare.data.GetCurrentDataResult;
import com.examples.android.healthcare.util.FileUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.FileNotFoundException;
import java.nio.file.Paths;
import java.util.List;
/**
* 健康管理Flaskアプリからのデータ取得リクエストのレスポンス(JSON)をJava Objectに変換する
* curlで取得したレスポンスを入力ファイルとする
* $ curl -G -d 'emailAddress=user1@examples.com&measurementDay=2023-03-10'
* http://dell-t7500.local:5000/healthcare/getcurrentdata
*/
public class ParseResponseDataJson {
static final String JSON_NAME = "response_getcurrentdata.json";
public static void main(String[] args) {
// 入力ファイル名
// ~/Documents/java/input/response_getcurrentdata.json
String readFile = Paths.get(Constants.INPUT_DATA_PATH, JSON_NAME).toString();
try {
// テスト用ファイルからJSON文字列を取得する
List<String> responseLines = FileUtil.readLines(readFile);
String responseJson = String.join("", responseLines);
Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
// Androidアプリで使用するデータ取得レスボンスデータクラス
GetCurrentDataResult respObj = gson.fromJson(responseJson, GetCurrentDataResult.class);
System.out.println(respObj);
} catch (FileNotFoundException e) {
System.out.println(e.getLocalizedMessage());
} catch (Exception e) {
// パースエラー
System.out.println(e.getLocalizedMessage());
}
}
}
GetCurrentDataResult{
data=RegisterData{
emailAddress='user1@examples.com', measurementDay='2023-03-01',
healthcareData=HealthcareData{
sleepManagement=SleepManagement{
wakeupTime='05:30', sleepScore=83, sleepingTime='06:40', deepSleepingTime='null'
},
bloodPressure=BloodPressure{
morningMeasurementTime=06:40, morningMax=119, morningMin=70, morningPulseRate=69,
eveningMeasurementTime=22:55, eveningMax=132, eveningMin=82, eveningPulseRate=61
},
bodyTemperature=BodyTemperature{
measurementTime='null', temperature=null
},
nocturiaFactors=NocturiaFactors{
midnightToiletVisits=0, hasCoffee=true, hasTea=false, hasAlcohol=false,
hasNutritionDrink=false, hasSportsDrink=false, hasDiuretic=false, takeMedicine=false,
takeBathing=false, conditionMemo='ちょっと風邪気味'
},
walkingCount=WalkingCount{
counts=7870
}
},
weatherData=WeatherData{
weatherCondition=weatherCondition{
condition='曇り'}
}
},
status=ResponseStatus{
code=0, message='OK'
}
}
package com.examples.android.healthcare;
import com.examples.android.healthcare.data.ResponseWarningStatus;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
* 健康管理FlaskアプリからのWarningレスポンスをJava Objectに変換する
* curlで取得したレスポンスを入力文字列とする ※未登録の日付を指定
* $ curl -G -d 'emailAddress=user1@examples.com&measurementDay=2023-04-10'
* http://dell-t7500.local:5000/healthcare/getcurrentdata
* {
* "status": {
* "code": 404,
* "message": "Data is not found."
* }
* }
*/
public class ParseResponseWarningJson {
// データ取得リクエストでレコード未登録時に返却されるレスポンス
static final String jsonText = "{\"status\": {\"code\": 404, \"message\": \"Data is not found.\" }}";
public static void main(String[] args) {
Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
try {
ResponseWarningStatus respObj = gson.fromJson(jsonText, ResponseWarningStatus.class);
System.out.println(respObj);
} catch (Exception e) {
System.out.println(e.getLocalizedMessage());
}
}
}
ResponseWarningStatus{status=ResponseStatus{code=404, message='Data is not found.'}}
/**
* GETリクエストで該当するデータを取得する
* @param emailAddress メールアドレス
* @param pastDay 過去の測定日付
*/
private void sendGetCurrentDataRequest(String emailAddress, String pastDay) {
RequestDevice device = NetworkUtil.getActiveNetworkDevice(getContext());
if (device == RequestDevice.NONE) {
showDialogNetworkUnavailable();
return;
}
showActionBarGetting(device);
HealthcareApplication app = (HealthcareApplication) requireActivity().getApplication();
String requestUrl = app.getmRequestUrls().get(device.toString());
Map headers = app.getRequestHeaders();
// GETリクエスト送信: 登録済みデータの取得
HealthcareRepository repository = new GetCurrentDataRepository();
String requestUrlWithPath = requestUrl + repository.getRequestPath(0);
// リクエストパラメータ: 主キー項目(メールアドレス, 測定日付)
String requestParams = AppTopUtil.getRequestParams(emailAddress, pastDay);
repository.makeGetRequest(0, requestUrl, requestParams, headers,
app.mEexecutor, app.mHandler, (result) -> {
// リクエストURLをAppBarに表示
showActionBarResult(requestUrlWithPath);
// 送信ボタンを戻す
mBtnSend.setEnabled(true);
if (result instanceof Result.Success) {
// ■■ 1.(1) 正常レスポンス(JSON)をGetCurrentDataResultオブジェクトとして取得
GetCurrentDataResult dataResult =
((Result.Success) result).get();
// ■■ 1.(2) プロパティ["data"]に格納されていた登録データを取得
RegisterData data = dataResult.getData();
// 古いオブジェクトを破棄する
if (mRegisterDataForUpdate != null) {
mRegisterDataForUpdate = null;
}
// https://www.baeldung.com/java-deep-copy
// 6.3. JSON Serialization With Jackson
Gson gson = new Gson();
// 各入力フィールドに変更があったかどうかを確認するためのオブジェクト
mRegisterDataForUpdate =
gson.fromJson(gson.toJson(data), RegisterData.class);
// ウィジット更新
updateInputWidgetsFromRegisterData(data); // ■■ 1.(3) 登録データを該当するウィジットに設定する
// ステータス更新
showStatus(getString(R.string.message_get_current_ok));
// 更新用モニター開始
startUpdateMonitor();
// 送信ボタンのラベルを"更新"に変更
changePostRequestWithButton(PostRequest.UPDATE);
} else if (result instanceof Result.Warning) {
// ■■ 2.(1) ウォーニングレスポンス(JSON)をResponseStatusオブジェクトとして取得
ResponseStatus status =
((Result.Warning>) result).getResponseStatus();
DEBUG_OUT.accept(TAG, "WarningStatus: " + status);
if (status.getCode() == 404) {
// 未登録(404)なら新規登録モードにリセット
mBtnSave.setEnabled(true);
resetToNewRegistrationMode();
}
showWarning(getResponseWarning(status)); // ■■ 2.(2) ウォーニングをステータスに表示
} else {
// 例外メッセージをダイアログに表示
Exception exception = ((Result.Error>) result).getException();
Log.w(TAG, "GET error:" + exception.toString());
String errorMessage = String.format(
getString(R.string.exception_with_reason),
exception.getLocalizedMessage());
showMessageDialog(getString(R.string.error_response_dialog_title),
errorMessage,"ExceptionFragment");
}
});
}
コードの一部のみ示します(データベースから取得するコードは省略) コメント■■部分がレスポンスのプロパティ["data"]になります
@app.route(APP_ROOT + "/getcurrentdata", methods=["GET"])
def getcurrentdata():
"""メールアドレスと測定日付から健康管理データを取得するリクエスト
:param: request parameter: ?emailAddress=user1@examples.com&measurementDay=2023-0-03
:return: JSON形式(健康管理DBから取得したデータ + 気象センサーDBから取得した天候)
"""
if app_logger_debug:
app_logger.debug(request.path)
app_logger.debug(request.args.to_dict())
# 1.リクエストパラメータチェック
# 1-1.メールアドレス
emailAddress = request.args.get("emailAddress")
if emailAddress is None:
abort(BadRequest.code, _set_errormessage("462,Required EmailAddress."))
# 1-2.測定日付
measurementDay = request.args.get("measurementDay")
if emailAddress is None:
abort(BadRequest.code, _set_errormessage("463,Required MeasurementDay."))
# PersonテーブルにemailAddressが存在するか
personId = _get_personid(emailAddress)
if personId is None:
abort(BadRequest.code, _set_errormessage("461,User is not found."))
# 健康管理DBと気象センサーDBからデータ取得する
selector = Selector(Cls_sess_healthcare, Cls_sess_sensors, logger=app_logger)
# 健康管理データ取得
healthcare_dict: Dict = selector.get_healthcare_asdict(emailAddress, measurementDay) # ■■ 辞書オブジェクト
if app_logger_debug:
app_logger.debug(f"Healthcare: {healthcare_dict}")
if healthcare_dict:
healthcare_dict["emailAddress"] = emailAddress
healthcare_dict["measurementDay"] = measurementDay
# 天気状態取得
weather_dict = selector.get_weather_asdict(measurementDay) # ■■ 辞書オブジェクト
if app_logger_debug:
app_logger.debug(f"Weather: {weather_dict}")
if weather_dict:
healthcare_dict["weatherData"] = weather_dict # ■■ 健康管理データ辞書オブジェクトに天候状態を追加する
else:
# 天候がなければ未設定
healthcare_dict["weatherData"] = None
return make_getdata_success(healthcare_dict) # ■■ JSONレスポンスを返却する
else:
abort(NotFound.code, _set_errormessage("Data is not found."))
def make_getdata_success(json_dict: Dict) -> Response:
"""
データ取得処理OKレスポンス
:param json_dict: JSON出力用辞書
:return: Response
"""
resp_obj: Dict = {
"status": {
"code": 0, "message": "OK"},
"data": json_dict # ■■ ["data"]プロパティに辞書オブジェクト(登録データ)を設定
}
return _make_respose(resp_obj, 200)
def make_register_success(email_address: str, measurement_day: str) -> Response:
"""
登録処理OKレスポンス
:param email_address: メールアドレス
:param measurement_day 測定日付
:return: Response
"""
resp_obj: Dict = { # ■■ レスポンス用辞書オブジェクト
"status":
{"code": 0, "message": "OK"},
"data": {
"emailAddress": email_address,
"measurementDay": measurement_day
}
}
return _make_respose(resp_obj, 200)
# ■■ エラーハンドラー: abort()関数から呼び出しされる
@app.errorhandler(BadRequest.code)
@app.errorhandler(Forbidden.code)
@app.errorhandler(NotFound.code)
@app.errorhandler(Conflict.code) # IntegrityError (登録済み)
@app.errorhandler(InternalServerError.code)
def error_handler(error: HTTPException) -> Response:
app_logger.warning(f"error_type:{type(error)}, {error}")
resp_obj: Dict[str, Dict[str, Union[int, str]]] = {
"status": {"code": error.code, "message": error.description["error_message"]} # ■■ウォーニングステータス
}
return _make_respose(resp_obj, error.code)