Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
36
Help us understand the problem. What is going on with this article?

More than 3 years have passed since last update.

@y___k

JavaScriptでつくるExcel→JSON変換アプリ

un-T factory! XA Advent Calendar 14日目の記事です。

はじめに

un-T System! の山中です。フロントエンドエンジニアです。

JavaScriptでMacアプリをつくることができるって知ってましたか?
物は試しと、ExcelをJSONに変換するアプリをつくってみました。
Appleによると、この技術はJXA(JavaScript for Automation)という呼び名らしいです。

きほん

開発にはMac(OS X Yosemite以降)に標準で入ってる「スクリプトエディタ」を使用します。
script_editor.png
すごくシンプルなエディタです。

  1. スクリプトエディタを立ち上げる
  2. 左上の言語メニューで 「JavaScript」を選択。(選べるのはAppleScriptかJavaScript)
  3. コードを書く
  4. ファイル → 書き出す(ファイルフォーマット:アプリケーション)

これだけでアプリをつくることができます。 簡単。

つくった

制作したアプリのコードはこちらです。
Excelファイルをドラッグ&ドロップすると、JSONファイルが書き出されます。
(注:実行にはMicrosoft Excelがインストールされている必要があります。)

JavaScript(JXA)

function analyzeExcel(path) {
  var app, worksheets;
  app = Application('Microsoft Excel');
  app.includeStandardAdditions = true;
  app.activate();
  app.open(path);
  worksheets = app.worksheets;

  // ワークシートごとにJSONをつくる
  for (var i = 0; i < worksheets.length; i++) {
    makeJSON(worksheets[i]);
  }

  function makeJSON(ws) {
    var data = {};
    var title = [];
    var first_row = ws.rows[0];
    var worksheet_name = ws.name();

    // 1行目を見出しとして取得
    for (var col_i = 0; ; col_i++) {
       var check = first_row.columns[col_i].value();
       if (!check) {
          break;
       }
       title.push(check);
    }

    // 2行目からデータとして取得
    for (var row_i = 1; ; row_i++) {
       var row = ws.rows[row_i];
       var row_data = {};
       // 1列目でデータの有無を判断
       var id = row.columns[0].value();
       if (!id) {
          break;
       } else {
          data[id] = {};
       }
       // 行ごとにデータをまとめる
       // 同じ見出しがあれば、配列にまとめる
       for (var i = 1; i < title.length; i++) {
          if (row_data[title[i]]) {
             // 配列化
             if (!Array.isArray(row_data[title[i]])) {
                row_data[title[i]] = [row_data[title[i]]];
             }
             // 追加
             if (row.columns[i].value()) {
                row_data[title[i]].push(row.columns[i].value());
             }
          } else {
             row_data[title[i]] = row.columns[i].value();
          }
       }
       data[id] = row_data;
    }

    // 書き出し設定
    var filePath = app.chooseFileName({
       defaultName: worksheet_name + '.json',
       defaultLocation: app.pathTo('desktop')
    });

    // JSONデータを書き出す(文字コードをUTF-8に変換)
    ObjC.import('Cocoa');
    var text = JSON.stringify(data, null, '  ');
    string = $.NSString.stringWithString(text);
    string.writeToFileAtomicallyEncodingError(
      filePath.toString(),
      true,
      $.NSUTF8StringEncoding,
      $()
    );
  }
}

// アプリにファイルをドラッグ&ドロップした時の処理
var SystemEvents = Application("System Events");
var fileTypesToProcess = ["ELSX"];
var extensionsToProcess = ["xlsx"];
var typeIdentifiersToProcess = [];
function openDocuments(droppedItems) {
  for (var item of droppedItems) {
    var alias = SystemEvents.aliases.byName(item.toString());
    var extension = alias.nameExtension();
    var fileType = alias.fileType();
    var typeIdentifier = alias.typeIdentifier();
    if (
       fileTypesToProcess.includes(fileType) 
       || extensionsToProcess.includes(extension)
       || typeIdentifiersToProcess.includes(typeIdentifier)
    ) {
      var path = Path(item.toString().slice(1));
      analyzeExcel(path);
    }
  }
}

// アプリのアイコンをダブルクリックした時に使い方を説明する
function run() {
  var sys = Application("System Events");
  sys.includeStandardAdditions = true;
  sys.displayDialog("Excelファイル(xlsx)をドラッグ&ドロップしてください。JSONファイルに変換します。");
}


以下が変換ルールです。

  • ExcelのワークシートごとにJSONファイルを作成
  • 1行目を見出し、2行目以降を値として扱う
  • 1列目の値をキーに使い、行ごとにデータをまとめる
  • 見出しが同じ場合、配列にする
  • UTF-8に変換する(変換しないと文字化け)

たとえば、このようなExcelファイルをドラッグ&ドロップすると…

table.png

このようなJSONが書き出されます!

JSON
{
  "a": {
    "name": "John",
    "num": [
      11,
      12,
      13
    ]
  },
  "b": {
    "name": "Paul",
    "num": [
      21,
      22
    ]
  },
  "c": {
    "name": "George",
    "num": [
      31
    ]
  },
  "d": {
    "name": "Ringo",
    "num": ""
  }
}

まとめ

求めている情報を探すのに時間がかかりました。(スクリプトエディタのライブラリは読みにくい!)
ただ、JavaScriptを使うことができるという利点は大きいです。
いろいろな事が出来るみたいなので、何か便利なものをつくりながら知見を貯めていこうと思います。

36
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
36
Help us understand the problem. What is going on with this article?