AndroidアプリとFlaskアプリ間JSONデータ交換


【最終更新日】2023-04-15

Androidアプリ(Java)で生成した登録用JSON文字列をFlaskアプリで受け取る方法について解説します。

Androidライブラリに依存しないPureなJavaクラスの開発とテストはAndroid Studioではなく通常のJavaIDEで開発したほうが効率的です。 プロジェクトを作成する時にJavaクラス群のパッケージをAndroidアプリのパッケージに合わせるだけで動作確認が終わったものはAndroid側プロジェクトにそのままコピーするだけですみます。

1.開発・テスト用のプロジェクト
(1)このページの説明で使われているJavaプロジェクトのクラス構成
java-health-care-example/
├── com
│   └── examples
│       └── android
│           └── healthcare
│               ├── Constants.java               (*) 入出力先定義
│               ├── OutputRegisterJson.java      (*) 登録用JSON出力メインクラス
│               ├── data                         (*) JSON出力を構成するデータクラス(Pure java)
│               │   ├── BloodPressure.java
│               │   ├── BodyTemperature.java
│               │   ├── HealthcareData.java
│               │   ├── NocturiaFactors.java
│               │   ├── RegisterData.java
│               │   ├── SleepManagement.java
│               │   ├── WalkingCount.java
│               │   ├── WeatherCondition.java
│               │   └── WeatherData.java
│               └── util
│                   └── FileUtil.java            (*) ファイル入出力クラス
└── lib
    └── gson-2.9.0.jar    (*) Mavenリポジトリからダウンロードしlibディレクトリに配置
Javaプロジェクトのソースコードはこちら
https://github.com/pipito-yukio/personal_healthcare/tree/main/src/java-health-care-example
(2)このページの説明で使われているFlaskプロジェクトのpythonファイル
Healthcare/
└── healthcare
    ├── __init__.py
    └── views
        ├── __init__.py
        └── app_main.py (*) アプリメイン (リクエスト処理)
Flaskアプリのソースコードはこちら
https://github.com/pipito-yukio/personal_healthcare/tree/main/src/webapp
2.JavaクラスとFlaskアプリ側辞書オブジェクト対応表
【登録データ用JSONを構成するJavaクラスと対応するFlaskアプリの辞書オブジェクト】
項 目プロパティ名JavaクラスFlaskアプリ側データ抽出方法
1.登録用データ RegisterData.java data = json.loads(request.data)
2.健康管理データ healthcareData HealthcareData.java healthcare_data = data["healthcareData"]
 (1) 睡眠管理データ sleepManagement SleepManagement.java sleep_man = healthcare_data["sleepManagement"]
 (2) 血圧測定データ bloodPressure BloodPressure.java blood_press_man = healthcare_data["bloodPressure"]
 (3) 体温測定データ bodyTemperature BodyTemperature.java body_temper = healthcare_data["bodyTemperature"]
 (4) 夜間頻尿要因データ nocturiaFactors NocturiaFactors.java nocturia_factors = healthcare_data["nocturiaFactors"]
 (5) 歩数データ walkingCount WalkingCount.java walking_count = healthcare_data["walkingCount"]
3.気象データ weatherData WeatherData.java weather_data = data["weatherData"]
 (1) 天候状態データ weatherCondition WeatherCondition.java weather_condition = weather_data["weatherCondition"]
3.JavaでJSON文字列を生成する

Javaクラスで定義するフィールド名をJSONのプロパティ名に合わせることで gsonライブラリがJSONプロパティとして出力してくれます。

1.登録用データ(健康管理データベース + 気象センサーデータベース) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class RegisterData {
    // 登録用主キー: emailAddress -> persion.id
    private final String emailAddress;
    // 登録用主キー: 測定日付
    private final String measurementDay;
    private final HealthcareData healthcareData; // 健康管理データベース
    private final WeatherData weatherData;       // 気象センサーデータベース

    public RegisterData(String emailAddress, String measurementDay,
                        HealthcareData healthcareData, WeatherData weatherData) {
        this.emailAddress = emailAddress;
        this.measurementDay = measurementDay;
        this.healthcareData = healthcareData;
        this.weatherData = weatherData;
    }

    public String getEmailAddress() { return emailAddress; }
    public String getMeasurementDay() { return measurementDay; }
    public HealthcareData getHealthcareData() { return healthcareData; }
    public WeatherData getWeatherData() { return weatherData; }
    //...省略...
}
2.健康管理データ(データベース用): 下記2 (1)〜(5)までのクラスを定義 ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class HealthcareData {
    private final SleepManagement sleepManagement;
    private final BloodPressure bloodPressure;
    private final BodyTemperature bodyTemperature;
    private final NocturiaFactors nocturiaFactors;
    private final WalkingCount walkingCount;

    public HealthcareData(SleepManagement sleepManagement,
                          BloodPressure bloodPressure,
                          BodyTemperature bodyTemperature,
                          NocturiaFactors nocturiaFactors,
                          WalkingCount walkingCount) {
        this.sleepManagement = sleepManagement;
        this.bloodPressure = bloodPressure;
        this.bodyTemperature = bodyTemperature;
        this.nocturiaFactors = nocturiaFactors;
        this.walkingCount = walkingCount;
    }

    public SleepManagement getSleepManagement() { return sleepManagement; }
    public BloodPressure getBloodPressure() { return bloodPressure; }
    public BodyTemperature getBodyTemperature() { return bodyTemperature; }
    public NocturiaFactors getNocturiaFactors() { return nocturiaFactors; }
    public WalkingCount getWalkingCount() { return walkingCount; }
    //...省略...
}
2 (1)睡眠管理データ(テーブル用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class SleepManagement {
    private final String wakeupTime;
    private final Integer sleepScore;
    private final String sleepingTime;
    private final String deepSleepingTime;

    public SleepManagement(String wakeupTime, Integer sleepScore,
                           String sleepingTime, String deepSleepingTime) {
        this.wakeupTime = wakeupTime;
        this.sleepScore = sleepScore;
        this.sleepingTime = sleepingTime;
        this.deepSleepingTime = deepSleepingTime;
    }

    public String getWakeupTime() { return wakeupTime; }
    public Integer getSleepScore() { return sleepScore; }
    public String getSleepingTime() { return sleepingTime; }
    public String getDeepSleepingTime() { return deepSleepingTime; }
    //...省略...
}
2 (2)血圧測定データ(テーブル用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class BloodPressure {
    private final String morningMeasurementTime;
    private final Integer morningMax;
    private final Integer morningMin;
    private final Integer morningPulseRate;
    private final String eveningMeasurementTime;
    private final Integer eveningMax;
    private final Integer eveningMin;
    private final Integer eveningPulseRate;

    public BloodPressure(String morningMeasurementTime,
                         Integer morningMax,
                         Integer morningMin,
                         Integer morningPulseRate,
                         String eveningMeasurementTime,
                         Integer eveningMax,
                         Integer eveningMin,
                         Integer eveningPulseRate) {
        this.morningMeasurementTime = morningMeasurementTime;
        this.morningMax = morningMax;
        this.morningMin = morningMin;
        this.morningPulseRate = morningPulseRate;
        this.eveningMeasurementTime = eveningMeasurementTime;
        this.eveningMax = eveningMax;
        this.eveningMin = eveningMin;
        this.eveningPulseRate = eveningPulseRate;
    }

    public String getMorningMeasurementTime() { return morningMeasurementTime; }
    public Integer getMorningMax() { return morningMax; }
    public Integer getMorningMin() { return morningMin; }
    public Integer getMorningPulseRate() { return morningPulseRate; }
    public String getEveningMeasurementTime() { return eveningMeasurementTime; }
    public Integer getEveningMax() { return eveningMax; }
    public Integer getEveningMin() { return eveningMin; }
    public Integer getEveningPulseRate() { return eveningPulseRate; }
    //...省略...
}
2 (3)体温測定データ(テーブル用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

import com.google.gson.annotations.SerializedName;

public class BodyTemperature {
    private final String measurementTime;
    private final Double temperature;

    public BodyTemperature(String measurementTime, Double temperature) {
        this.measurementTime = measurementTime;
        this.temperature = temperature;
    }

    public String getMeasurementTime() { return measurementTime; }
    public Double getTemperature() { return temperature; }
    //...省略...
}
2 (4)夜間頻尿要因データ(テーブル用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class NocturiaFactors {
    private final int midnightToiletVisits;
    private final boolean hasCoffee;
    private final boolean hasTea;
    private final boolean hasAlcohol;
    private final boolean hasNutritionDrink;
    private final boolean hasSportsDrink;
    private final boolean hasDiuretic;
    private final boolean takeMedicine;
    private final boolean takeBathing;
    private final String conditionMemo;

    public NocturiaFactors(int midnightToiletVisits,
           boolean hasCoffee, boolean hasTea, boolean hasAlcohol, boolean hasNutritionDrink,
           boolean hasSportsDrink, boolean hasDiuretic, boolean takeMedicine, boolean takeBathing,
           String conditionMemo) {
        this.midnightToiletVisits = midnightToiletVisits;
        this.hasCoffee = hasCoffee;
        this.hasTea = hasTea;
        this.hasAlcohol = hasAlcohol;
        this.hasNutritionDrink = hasNutritionDrink;
        this.hasSportsDrink = hasSportsDrink;
        this.hasDiuretic = hasDiuretic;
        this.takeMedicine = takeMedicine;
        this.takeBathing = takeBathing;
        this.conditionMemo = conditionMemo;
    }

    public int getMidnightToiletVisits() { return midnightToiletVisits; }
    public boolean hasCoffee() { return hasCoffee; }
    public boolean hasTea() { return hasTea; }
    public boolean hasAlcohol() { return hasAlcohol; }
    public boolean hasNutritionDrink() { return hasNutritionDrink; }
    public boolean hasSportsDrink() { return hasSportsDrink; }
    public boolean hasDiuretic() { return hasDiuretic; }
    public boolean isTakeMedicine() { return takeMedicine; }
    public boolean isTakeBathing() { return takeBathing; }
    public String getConditionMemo() { return conditionMemo; }
    //...省略...
}
2 (5)歩数データ(テーブル用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class WalkingCount {
    private final Integer counts;

    public WalkingCount(Integer counts) {
        this.counts = counts;
    }

    public Integer getCounts() { return counts; }
    //...省略...
}
3.気象データ(気象センサーデータベース用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class WeatherData {
    private final WeatherCondition weatherCondition;

    public WeatherData(WeatherCondition wc) {
        this.weatherCondition = wc;
    }

    public WeatherCondition getWeatherCondition() {return weatherCondition; }

    //...省略...
}
3 (1)天候状態データ(テーブル用) ※toString()メソッドは省略
package com.examples.android.healthcare.data;

public class WeatherCondition {
    private final String condition;

    public WeatherCondition(String condition) {
        this.condition = condition;
    }

    public String getCondition() { return condition; }
    //...省略...
}
4 (1).Javaプロジェットのメインクラスで使用する入出力パス定義クラス
package com.examples.android.healthcare;

import java.nio.file.Paths;

/**
 * ユーザDocumentsディレクトリの入出力先 for linux: ~/Documents
 */
public class Constants {
    public static final String USER_HOME = System.getProperty("user.home");
    public static final String DOCUMENTS = Paths.get(USER_HOME, "Documents").toString();
    // 出力先パス: ~/Documents/java/output
    public static final String OUTPUT_DATA_PATH = Paths.get(DOCUMENTS, "java", "output").toString();
    // 入力データパス: ~/Documents/java/input
    public static final String INPUT_DATA_PATH =  Paths.get(DOCUMENTS, "java", "input").toString();
}
4 (2).ファイル入出力クラス
package com.examples.android.healthcare.util;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class FileUtil {
    // テキストファイル保存
    public static void saveText(String fileName, String data) throws IOException {
        FileOutputStream fos = new FileOutputStream(fileName);
        try (BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
            writer.write(data);
            writer.newLine();
        }
    }
    //...省略...
}
4 (3).メインクラスで登録用JSONを出力する
package com.examples.android.healthcare;

import com.examples.android.healthcare.data.*;
import com.examples.android.healthcare.util.FileUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;
import java.nio.file.Paths;

/**
 * 健康管理Flaskアプリへの登録用データの出力テスト
 * 出力したJSONファイルを用いてcurlコマンドで健康管理Flaskアプリに登録リクエストを実行する
 * (使用例) cd ~/Documents/java/output
 *  $ curl -X POST -H "Content-type: application/json" -d @register_data.json "dell-t7500.local:5000/healthcare/register"
 */
public class OutputRegisterJson {
    static final String JSON_NAME = "register_data.json";

    public static void main(String[] args) {
        // 出力ファイル名
        // ~/Documents/java/output/register_data.json
        String saveFile = Paths.get(Constants.OUTPUT_DATA_PATH, JSON_NAME).toString();
        // Gson gson = new GsonBuilder().serializeNulls().create();
        // 整形して出力
        Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();

        // 睡眠管理データ
        SleepManagement sleepManagement = new SleepManagement(
           "05:30", 83, "06:40", "0:50"
        );
        // 血圧管理データ
        BloodPressure bloodPressure = new BloodPressure(
           "06:50", 124, 76, 73,
           "22:15", 137, 80, 57
        );
        // 体温測定データ
        BodyTemperature bodyTemperature = new BodyTemperature(
           null, null
        );
        // 夜間頻尿要因データ
        NocturiaFactors factors = new NocturiaFactors(
           1,true, false, false, false, false,
            false,false, true, "今日は疲れた"
        );
        // 歩数データ
        WalkingCount count = new WalkingCount(8250);
        // 健康管理データ (健康管理データベース用)
        HealthcareData healthcareData = new HealthcareData(
             sleepManagement,
             bloodPressure,
             bodyTemperature,
             factors,
             count
        );
        // 天気情報 (気象センサーデータベース用)
        WeatherCondition wc = new WeatherCondition("晴れのち曇り");
        WeatherData weatherData = new WeatherData(wc);
        // 登録データ
        RegisterData regsterData = new RegisterData(
                "user1@examples.com",
                "2023-03-10",
                healthcareData,
                weatherData
        );
        // JavaオブジェクトをJSON文字列に変換
        String jsonText = gson.toJson(regsterData);
        try {
            // 出力先に保存
            FileUtil.saveText(saveFile, jsonText);
        } catch (IOException e) {
            System.out.println(e);
        }
    };
}
5.出力された登録用JSONファイル (register_data.json)
{
  "emailAddress": "user1@examples.com",
  "measurementDay": "2023-03-10",
  "healthcareData": {
    "sleepManagement": {
      "wakeupTime": "05:30",
      "sleepScore": 83,
      "sleepingTime": "06:40",
      "deepSleepingTime": "0:50"
    },
    "bloodPressure": {
      "morningMeasurementTime": "06:50",
      "morningMax": 124,
      "morningMin": 76,
      "morningPulseRate": 73,
      "eveningMeasurementTime": "22:15",
      "eveningMax": 137,
      "eveningMin": 80,
      "eveningPulseRate": 57
    },
    "bodyTemperature": {
      "measurementTime": null,
      "temperature": null
    },
    "nocturiaFactors": {
      "midnightToiletVisits": 1,
      "hasCoffee": true,
      "hasTea": false,
      "hasAlcohol": false,
      "hasNutritionDrink": false,
      "hasSportsDrink": false,
      "hasDiuretic": false,
      "takeMedicine": false,
      "takeBathing": true,
      "conditionMemo": "今日は疲れた"
    },
    "walkingCount": {
      "counts": 8250
    }
  },
  "weatherData": {
    "weatherCondition": {
      "condition": "晴れのち曇り"
    }
  }
}
4.Flaskアプリで登録データ(JSON)を受信し各テーブル用のデータを取得する

コードの一部のみ示します(テーブル登録処理は省略) ■■部分(9箇所)がデータを取り出す処理となります

(1)登録データPOST処理
@app.route(APP_ROOT + "/register", methods=["POST"])
def register():
    """
    健康管理データ(必須)と天候データ(必須)の登録
    """
    if app_logger_debug:
        app_logger.debug(request.path)

    # 登録データチェック
    personId, emailAddress, measurementDay, data = _check_postdata(request)
    
    # 健康管理データ登録 (必須)
    _insert_healthdata(personId, measurementDay, data)
    # 気象データ登録 (必須) ※気象センサーデータベース
    _insert_weather(measurementDay, data)

    # ここまでエラーがなければOKレスポンス
    return make_register_success(emailAddress, measurementDay)
    #...一部省略...

def _check_postdata(request):
    # リクエストヘッダーチェック
    if "application/json" not in request.headers["Content-Type"]:
        abort(BadRequest.code, _set_errormessage("450,Bad request Content-Type."))

    # 登録用データ取得 
    data: dict = json.loads(request.data) # ■■ リクエストのPOSTデータをJSON化 ■■ 
    if app_logger_debug:
        app_logger.debug(data)

    # メールアドレスチェック
    emailAddress = data.get("emailAddress", None)
    if emailAddress is None:
        abort(BadRequest.code, _set_errormessage("462,Required EmailAddress."))

    # 測定日付チェック
    measurementDay = data.get("measurementDay", None)
    if measurementDay 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."))

    return personId, emailAddress, measurementDay, data # 結果をタプルで返却

def _insert_healthdata(person_id: int, measurement_day: str, data: Dict) -> None:
    """
    健康管理データの登録処理
    :param person_id メールアドレスから取得したPersion.id
    :param measurement_day: 測定日
    :data 登録用データ
    """
    # JSONキーチェック: 登録データは全てのデータ項目が必須 ※(1)〜(5)のいずれかが掛けていた場合はエラー
    try:
        # 健康管理データコンテナ
        healthcare_data: Dict = data["healthcareData"]              # ■■
        # (1) 睡眠管理
        sleep_man: Dict = healthcare_data["sleepManagement"]        # ■■
        # (2) 血圧測定
        blood_press: Dict = healthcare_data["bloodPressure"]        # ■■
        # (3) 夜中トイレ回数要因
        nocturia_factors: Dict = healthcare_data["nocturiaFactors"] # ■■
        # (4) 歩数
        walking_count: Dict = healthcare_data["walkingCount"]       # ■■
        # (5) 体温データ
        body_temper: Dict = healthcare_data["bodyTemperature"]      # ■■
    except KeyError as err:
        app_logger.warning(err)
        abort(BadRequest.code, _set_errormessage("460,{err}"))

    #...テーブルへの登録処理は省略...


def _insert_weather(measurement_day: str, data: Dict) -> None:
    """
    天候状態の登録処理
    :param measurement_day: 測定日
    :data 登録用データ (必須)
    """
    try:
        # 天候データコンテナは必ずある
        weather_data: Dict = data["weatherData"]                   # ■■
        # 天候状態は必須項目
        weather_condition: Dict = weather_data["weatherCondition"] # ■■
    except KeyError as err:
        app_logger.warning(err)
        return

    #...テーブルへの登録処理は省略...
メニューページへ
戻る