今回の記事は、
こちらの記事に触発されて書いたものです。
記事の中で紹介されているコードを参考にしつつ、jsonやtsv形式のファイルを出力できるようにいじってみました。
VScodeの拡張機能であるdrawioについて、詳しくは、次の記事をご参照ください:
1. 作成したdrawio
ざっくりと主キー、外部キー、そのほかといった分類でフィールドを分けています。
作成した.drawio
拡張子のファイルからXMLを読み取る方法
大きく分けて2つ方法があり、
- ER図を作成後の
XXX.drawio
ファイルの拡張子.drawio
をXXX.xml
のように拡張子.xml
に変更する -
.drawio
をテキストエディタで開きなおす。以下で手順を紹介。
手順1:画面上部のファイル名が表示されている部分で、右クリックを押下。
手順2:以下画像の、「Reopen Editor With...」を選択。(日本語だともう少しわかりやすいかも?)
手順3:コマンドパレットのプルダウンから「Text-Editor」を選択。
すると、XMLを確認することができる。(見づらいのでキャプチャはなしで。百聞は一見に如かずということで。)
2. 処理の概要
XMLの中身を確認してみると、次のようになっていました:
- テーブル名について:
styleの中にshape=table;が含まれているかつvalue属性に値あり(これがテーブル名にあたる) - テーブル名とフィールドの紐づけのための箱?:
styleの中にshape=table;が含まれているかつvalue属性に値なし(これは行にあたる) - フィールドのプロパティについて:
styleの中にshape=partialRectangle;が含まれている(これがフィールドのプロパティにあたる)
1.で「Customers」などのテーブル名を取得し、3.でテーブルのフィールドのプロパティ等を取得(主キーや外部キーの分別も可能)、そしてテーブルとフィールドを紐づける中間的なオブジェクトを2.で取得しているといったイメージ。
3. 実際のコード
(自分が)わかりやすくするために、変数を多めに設定してコードを記載:
import os
import json
import xml.etree.ElementTree as ET
from datetime import datetime
# 元ネタ:https://qiita.com/moto3tt08/items/8ef4a3ffbe3fdce71537
now = datetime.strftime(datetime.now(), "%Y%m%d%H%M%S%f")
dirname = os.path.dirname(__file__)
tables = []
rows = []
fields = []
for file in os.listdir(dirname):
basename, extension = os.path.splitext(file) # file名と拡張子を分離→タプル
if extension == ".xml":
root_tree = ET.parse(os.path.join(dirname, file)).getroot() # XMLのルートツリーを取得
for cell in root_tree.iter("mxCell"):
table = {}
row = {}
field = {}
id = cell.get("id", "")
parent = cell.get("parent", "")
value = cell.get("value", "")
style = cell.get("style", "")
if "shape=table;" in style:
table.update({"id": id, "table": value})
elif "shape=tableRow;" in style:
row.update({"id": id, "parent": parent})
elif "shape=partialRectangle;" in style:
if "PK" == value:
field.update({"id": id, "parent": parent, "PK": value})
elif "FK" == value:
field.update({"id": id, "parent": parent, "FK": value})
else:
field.update({"id": id, "parent": parent, "FIELD": value})
if table:
tables.append(table)
if row:
rows.append(row)
if field:
fields.append(field)
header = ["table_name", "field(pk)", "field(fk)", "field(others)"]
for row in rows:
row_id = row.get("id")
for field in fields:
if row_id == field.get("parent"):
field.pop("parent")
row.update(field)
records_dict = [] # dict形式でテーブルを整理
records_comma = [] # カンマ区切りでテーブルを整理(複数フィールドがある場合は、パイプ区切り"|")
records_tab = [] # タブ区切りでテーブルを整理(複数フィールドがある場合は、パイプ区切り"|")
for table in tables:
id = table.get("id")
table_name = table.get("table")
field_pk = []
field_fk = []
field_others = []
for row in rows:
if id == row.get("parent"):
if row.get("PK",""):
field_pk.append(row.get("FIELD"))
elif row.get("FK",""):
field_fk.append(row.get("FIELD"))
else:
field_others.append(row.get("FIELD"))
records_dict.append({
"table_name": table_name,
"field(pk)": field_pk,
"field(fk)": field_fk,
"field(others)": field_others
})
records_comma.append(
",".join([table_name, "|".join(field_pk), "|".join(field_fk), "|".join(field_others)])
)
records_tab.append(
"\t".join([table_name, "|".join(field_pk), "|".join(field_fk), "|".join(field_others)])
)
json_file = f"{now}.json" # jsonファイル名
csv_file = f"{now}.csv" # csvファイル名
tsv_file = f"{now}.tsv" # tsvファイル名
with open(os.path.join(dirname, json_file), "w", encoding="utf-8") as w:
w.write(json.dumps(records_dict,indent=4, ensure_ascii=False))
with open(os.path.join(dirname, csv_file), "w", encoding="utf-8") as w:
w.write(",".join(header)+"\n")
[w.write(record+"\n") for record in records_comma]
with open(os.path.join(dirname, tsv_file), "w", encoding="utf-8") as w:
w.write("\t".join(header)+"\n")
[w.write(record+"\n") for record in records_tab]
※VScodeの設定でタブをスペースに置換している場合、Excelに張り付けてもタブ区切りで分割できない。そのため参照のページで設定を変更する必要あり。(参照:https://boonx4m312s.hatenablog.com/entry/2022/06/24/210000)
上記コードを実行すると、json, csv, tsv
の3つのファイルが作成されるはずです。以下はサンプルです。:
[
{
"table_name": "Customers",
"field(pk)": [
"customer_id int NOT NULL"
],
"field(fk)": [],
"field(others)": [
"customer_name char(50) NOT NULL",
"Row 2",
"Row 3"
]
},
{
"table_name": "Orders",
"field(pk)": [
"order_id int NOT NULL"
],
"field(fk)": [
"customer_id int NOT NULL"
],
"field(others)": [
"order_date date NOT NULL",
"Row 3"
]
},
{
"table_name": "Shipments",
"field(pk)": [
"shipment_id int NOT NULL"
],
"field(fk)": [
"order_id int NOT NULL"
],
"field(others)": [
"shipment_date date NOT NULL",
"Row 3"
]
}
]
table_name,field(pk),field(fk),field(others)
Customers,customer_id int NOT NULL,,customer_name char(50) NOT NULL|Row 2|Row 3
Orders,order_id int NOT NULL,customer_id int NOT NULL,order_date date NOT NULL|Row 3
Shipments,shipment_id int NOT NULL,order_id int NOT NULL,shipment_date date NOT NULL|Row 3
table_name field(pk) field(fk) field(others)
Customers customer_id int NOT NULL customer_name char(50) NOT NULL|Row 2|Row 3
Orders order_id int NOT NULL customer_id int NOT NULL order_date date NOT NULL|Row 3
Shipments shipment_id int NOT NULL order_id int NOT NULL shipment_date date NOT NULL|Row 3
※複数のフィールドがある場合は、パイプ区切りで表現しています。
4. まとめ
今回はXMLからcsv, tsvおよびjsonファイルを作成するコードを紹介しました。
次回は、csvまたはtsvファイルなどからXMLを作成し、作図ができないか検証をしてみようと思います。