【Flutter】excelマクロで作成したjsonファイルを読み込んでみる

2024/04/19に公開

概要

製造部で使用するようなexcelファイルにマクロを埋め込んで、マクロからjsonファイルを作成しFlutterのアプリ実行から設定値を読み込んでみました。

1. excelファイルからjsonファイル作成

1.1. excelファイル
下記のように製造部が入力するタブとマクロを実行するタブを作成しました。
また、データ入力を試すために、bool型・int型・List<int>型・double型・String型・List<String>型でそれぞれ行ってみました。

セルの計算式については、一部コメントで表示しておきます(本当は、githubでファイルをダウンロードできるようにするのがよいのでしょうが…)。コメントの「あり・なし_範囲」はありとなしをtrue/falseに割り付けたテーブルを参照しているところです(選択テーブルは省略)。

1.2. excelファイルのマクロ
作成ボタンを押すと、文字コードUTF-8でBOM消してjsonファイルを作成するようにマクロを作成しました。BOMについては他のHPを参照して下さい。マクロコードについては【EXCEL VBA】BOM無しUTF-8でCSVファイル出力をしたいを参考にさせて頂きました。


Sub ファイル作成_Click()

    '警告表示無効:上書き警告を表示しない
    Application.DisplayAlerts = False
    
    'ファイル出力:設定表.xlsmと同じパスにファイルを作成
        
    Dim ret As Boolean
    ret = WriteUTF8(ThisWorkbook.Path, "test.json")
    
    
    'メッセージボックス表示時の砂時計表示防止:矢印型ポインタに変更
    Application.Cursor = xlNorthwestArrow
    If ret = True Then
        'メッセージボックス表示
        MsgBox ("test.json を作成しました  ")
    Else
        'メッセージボックス表示
        MsgBox ("失敗しました  ")
    End If
    '標準ポインタに変更
    Application.Cursor = xlDefault
    
    'プロパティ設定表.xlsmファイルを閉じるときの保存確認のメッセージを表示させない
    ActiveWorkbook.Saved = True
    
    '警告表示有効
    Application.DisplayAlerts = True

End Sub

'UTF8形式でファイル作成
Public Function WriteUTF8(dir As String, filename As String) As Boolean
    WriteUTF8 = False
    Dim A, B
    A = dir & "\" & filename
    
    On Error GoTo err
    
    Dim adb As New ADODB.stream
    With adb
        .Type = adTypeText
        .Charset = "UTF-8"
        .Open
    End With
        
    'ファイル書込み
    With ActiveSheet.UsedRange
        For i = 1 To .Rows.Count
            adb.WriteText Cells(i, 1), 1
        Next
    End With
    'BOMを削除する
    adb.Position = 0
    adb.Type = 1
    adb.Position = 3
    B = adb.Read
    adb.Close
    adb.Open
    adb.Write B
    '保存するファイルを指定
    adb.SaveToFile A, 2
    adb.Close
    
    WriteUTF8 = True
    
err:
    
End Function

1.3. jsonファイル作成
作成ボタンを押してjsonファイルを作成します。

BOMが残った場合、適当なバイナリエディタソフトで見てみると下記のような部分があります。これがあるとプログラム上で読み込めないということがあるので注意します。

2.アプリ実装

【Flutter】JSON形式でローカルファイルにデータ保存(json_serializable)で紹介(大変分かりやすくて助かりました。ありがとうございました!!)されているコードとほとんど一緒ですが、excelファイルの各データにあわせるため一部改変しています。

2.1. パッケージインストール

 flutter pub add json_annotation path_provider build_runner json_serializable
 flutter pub get

2.2. pubspec.yaml編集
build_runnerとjson_serializableをdev_dependenciesの下に記載し直す。

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  json_annotation: ^4.8.1
  path_provider: ^2.1.3

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0
  build_runner: ^2.4.9
  json_serializable: ^6.7.1

2.3. json_setting.dart作成
jsonデータを入力するクラスを作成。作成した後にエラーが出ていますが2.4を実行するまで問題はありません。

json_setting.dart
import 'package:json_annotation/json_annotation.dart';
part 'json_settings.g.dart'; // flutter pub run build_runner build --delete-conflicting-outputs(後述)で生成されるファイル名

@JsonSerializable()
class JsonSettings {
  // セーブしたい情報を変数にする
  final bool bool_value;
  final int int_value;
  final List<int> int_list;
  final double double_value;
  final String string_value;
  final List<String> string_list;

  JsonSettings(
      {required this.bool_value,
      required this.int_value,
      required this.int_list,
      required this.double_value,
      required this.string_value,
      required this.string_list});
  factory JsonSettings.fromJson(Map<String, dynamic> json) =>
      _$JsonSettingsFromJson(json);
  Map<String, dynamic> toJson() => _$JsonSettingsToJson(this);
}

2.4. ターミナル実行
下記コマンドをターミナルで実行すると、下記クラスが作成されます。

 flutter pub run build_runner build --delete-conflicting-outputs
json_settings.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'json_settings.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

JsonSettings _$JsonSettingsFromJson(Map<String, dynamic> json) => JsonSettings(
      bool_value: json['bool_value'] as bool,
      int_value: json['int_value'] as int,
      int_list:
          (json['int_list'] as List<dynamic>).map((e) => e as int).toList(),
      double_value: (json['double_value'] as num).toDouble(),
      string_value: json['string_value'] as String,
      string_list: (json['string_list'] as List<dynamic>)
          .map((e) => e as String)
          .toList(),
    );

Map<String, dynamic> _$JsonSettingsToJson(JsonSettings instance) =>
    <String, dynamic>{
      'bool_value': instance.bool_value,
      'int_value': instance.int_value,
      'int_list': instance.int_list,
      'double_value': instance.double_value,
      'string_value': instance.string_value,
      'string_list': instance.string_list,
    };

  1. main.dart
main.dart
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:json_settings_sample2/json_settings.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  final app = MaterialApp(
    theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      useMaterial3: true,
    ),
    home: const MyApp(),
  );

  runApp(app);
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String output = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              // セーブボタン
              onPressed: () async {
                // クラス生成
                final sd1 = JsonSettings(
                  bool_value: true,
                  int_value: 2,
                  int_list: [3, 5, 8],
                  double_value: 2.57,
                  string_value: "こんにちは",
                  string_list: ["ab", "149"],
                );

                // セーブするファイル名を決定
                final directory = await getExternalStorageDirectory();
                final path = '${directory?.path}/test.json';
                final file = File(path);
                // print('出力パス = $path');

                // String型に変換して保存
                final jsonString = json.encode(sd1.toJson());
                file.writeAsStringSync(jsonString);
                // print('書き込み内容 = $jsonString');

                // 表示するテキスト
                setState(() {
                  output =
                      'セーブ(bool = ${sd1.bool_value}\n, int = ${sd1.int_value}\n, int_list = ${sd1.int_list}\n, double = ${sd1.double_value}\n, String = ${sd1.string_value}\n, String_list = ${sd1.string_list}\n )';
                });
              },
              child: const Text('Save'),
            ),

            // ロード
            ElevatedButton(
              onPressed: () async {
                // 参照するファイルを取得
                final directory = await getExternalStorageDirectory();
                final path = directory?.path;
                final file = File('${path}/test.json');

                // データ読み込み
                final jsonString = file.readAsStringSync();
                print("json=$jsonString");
                final sd2 = JsonSettings.fromJson(jsonDecode(jsonString));

                // 表示するテキスト
                setState(() {
                  output =
                      'ロード(bool = ${sd2.bool_value}\n, int = ${sd2.int_value}\n, int_list = ${sd2.int_list}\n, double = ${sd2.double_value}\n, String = ${sd2.string_value}\n, String_list = ${sd2.string_list}\n )';
                });
              },
              child: const Text('load'),
            ),

            // outputを表示
            Text(
              'output\n$output',
              style: const TextStyle(
                fontSize: 30,
              ),
            )
          ],
        ),
      ),
    );
  }
}
  1. 実行
    アプリケーション内フォルダにexcelから作成したjsonファイルを入れ、アプリを実行します。
    loadボタンを押すとjsonデータをロードして表示されます。

おわりに

以上、excelマクロで作成したjsonファイルを読み込んでみるという記事でした。最後まで読んで頂きましてありがとうございました。
今回はjson形式でファイルを作成しましたが、正直なところdartで簡単にロードできるのならどのデータ形式でも構わないのですが、今までjsonファイルを設定ファイルとして使用してきたことがなかったので、いい勉強になりました。
また、mainコードでjsonデータをロードをしていますが、設定ファイル内のデータがプログラム内のデータとすべて一致する場合には問題なく動作します。ですが、設定ファイル内のデータが足りない場合はたぶんエラーが起きるんじゃないか(試していません)と思います。実際に使用する場合は、一部データがない場合も想定しデフォルト値を入れるような処理をする部分を作成する必要があるんじゃないかと思います(もしくは、assetsにデフォルトファイルを用意するとか)。

参考

https://pub.dev/packages/path_provider
https://pub.dev/packages/json_annotation
https://pub.dev/packages/json_serializable
https://pub.dev/packages/build_runner
https://magnetech.hatenablog.com/entry/json_save
https://atmarkit.itmedia.co.jp/aig/01xml/bom.html
https://vba-labo.rs-techdev.com/archives/1163

Discussion