使用Python解析FineReport模板數(shù)據(jù)集
背景
在使用 FineReport 作為報(bào)表平臺(tái)過(guò)程中,
當(dāng)報(bào)表項(xiàng)目過(guò)多,或者報(bào)表項(xiàng)目需要遷移,又或者報(bào)表數(shù)據(jù)源需要遷移時(shí),
我們通常需要知道報(bào)表用到的表有哪些,或者需要修改的SQL語(yǔ)句有哪些。
這時(shí)我們?cè)?FineReport 設(shè)計(jì)器中需要將模板的數(shù)據(jù)集提取出來(lái)。
但目前設(shè)計(jì)器中沒(méi)有 數(shù)據(jù)集導(dǎo)出 的功能。
所以我們使用Python 開(kāi)發(fā)程式以解析報(bào)表模板文件獲取 數(shù)據(jù)集 和 SQL語(yǔ)句,并保存為Excel,
以提供給后續(xù)解析SQL獲取調(diào)用內(nèi)容的項(xiàng)目使用。
應(yīng)用場(chǎng)景
- 查找調(diào)用內(nèi)容
- FineReport 模板遷移
- 元數(shù)據(jù)
- 數(shù)據(jù)血緣
操作對(duì)象
本示例中使用 本地工作目錄 中的 報(bào)表模板,F(xiàn)ineReport 平臺(tái)版本為 11
本地工作目錄路徑
D:\FineReport_11\webapps\webroot\WEB-INF\reportlets
示例報(bào)表模板路徑
01_dev\record_monitor\record_scheduler.cpt
示例報(bào)表模板數(shù)據(jù)集名稱(chēng)
log_record_execute
示例報(bào)表模板數(shù)據(jù)鏈接名稱(chēng)
LogDB
示例報(bào)表模板截圖

解析過(guò)程
查找目標(biāo)模板文件
為保證數(shù)據(jù)和模板安全,請(qǐng)先下載 正式環(huán)境reports文件夾,在本地進(jìn)行匹配
工作過(guò)程中,平臺(tái)的報(bào)表目錄不一定之后報(bào)表模板文件,故需要用代碼先查找匹配,再打開(kāi);
代碼
import pathlib
# 請(qǐng)先下載 正式環(huán)境reports文件夾,在本地進(jìn)行匹配
work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
# 遞歸獲取 工作目錄 本路徑和子路徑所有擴(kuò)展名為 .cpt 的 文件
for f in top.rglob("*.cpt"):
# 模板文件的絕對(duì)路徑
abs_path = f.absolute()
# 為保證對(duì)象準(zhǔn)確,使用絕對(duì)路徑
print(abs_path.__str__())
運(yùn)行結(jié)果
D:\FineReport_11\webapps\webroot\WEB-INF\reportlets\01_dev\record_monitor\record_scheduler.cpt
獲取 數(shù)據(jù)集
FineReport 報(bào)表模板 .cpt .frm 本身是 xml 文件,故可直接使用解析XML的方式解析;
解析方式是XPATH語(yǔ)法定位目標(biāo)元素
代碼
import pathlib
from lxml import etree
# 請(qǐng)先下載 正式環(huán)境reports文件夾,在本地進(jìn)行匹配
work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
# 解析結(jié)果 收集
result_list = []
# 遞歸獲取 工作目錄 本路徑和子路徑所有擴(kuò)展名為 .cpt 的 文件
for f in top.rglob("*.cpt"):
# 模板文件的絕對(duì)路徑
abs_path = f.absolute()
with open(abs_path, mode="rb") as fr:
xslt_content = fr.read()
# 開(kāi)始解析 模板文件 XML
xml_root = etree.XML(xslt_content)
# 使用 XPATH 定位 XML 中 數(shù)據(jù)集內(nèi)容
tabel_data_el = xml_root.xpath("http://*/TableDataMap//TableData[@class='com.fr.data.impl.DBTableData']")
for db_data in tabel_data_el:
# 數(shù)據(jù)集名稱(chēng)
date_source_name = db_data.get("name")
print(date_source_name)
運(yùn)行結(jié)果
log_record_execute
我們可以用 Vs Code 或者其他文本編輯器從資源管理器打開(kāi)這個(gè)模板文件,
可以看到 數(shù)據(jù)集 在模板X(qián)ML中的樣子,
數(shù)據(jù)集都在 標(biāo)簽名為 TableDataMap 的 xml 元素下,
數(shù)據(jù)集本身就是 標(biāo)簽名為 TableData 的 xml 元素,所以使用 XPATH 能夠根據(jù)這種結(jié)構(gòu)定位到。
同時(shí)數(shù)據(jù)集的內(nèi)容也能以相同方式定位到。
部分 XML
<?xml version="1.0" encoding="UTF-8"?>
<WorkBook xmlVersion="20211223" releaseVersion="11.0.0">
<TableDataMap>
<TableData name="log_record_execute" class="com.fr.data.impl.DBTableData">
<Parameters>
<Parameter>
<Attributes name="start_date"/>
<O>
<![CDATA[2023-07-01 00:00:00]]></O>
</Parameter>
<Parameter>
<Attributes name="end_date"/>
<O>
<![CDATA[2023-09-07 00:00:00]]></O>
</Parameter>
</Parameters>
<Attributes maxMemRowCount="-1"/>
<Connection class="com.fr.data.impl.NameDatabaseConnection">
<DatabaseName>
<![CDATA[LogDB]]></DatabaseName>
</Connection>
<Query>
<![CDATA[select * from fine_record_execute where todate(time) >= '${start_date}' and todate(time) <= '${end_date}']]></Query>
<PageQuery>
<![CDATA[]]></PageQuery>
</TableData>
</TableDataMap>
...
</WorkBook>
獲取數(shù)據(jù)集下的內(nèi)容
獲取到數(shù)據(jù)集的XML之后,變不需要從整個(gè)模板文件定位了,因?yàn)閿?shù)據(jù)集的各種內(nèi)容都在這串XML里了,
所以直接解析 數(shù)據(jù)集XML 的 字符串就行;
代碼
import json
import pathlib
from lxml import etree
# 請(qǐng)先下載 正式環(huán)境reports文件夾,在本地進(jìn)行匹配
work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
# 遞歸獲取 工作目錄 本路徑和子路徑所有擴(kuò)展名為 .cpt 的 文件
for f in top.rglob("*.cpt"):
# 模板文件的絕對(duì)路徑
abs_path = f.absolute()
with open(abs_path, mode="rb") as fr:
xslt_content = fr.read()
# 開(kāi)始解析 模板文件 XML
xml_root = etree.XML(xslt_content)
# 使用 XPATH 定位 XML 中 數(shù)據(jù)集內(nèi)容
tabel_data_el = xml_root.xpath("http://*/TableDataMap//TableData[@class='com.fr.data.impl.DBTableData']")
for db_data in tabel_data_el:
# 數(shù)據(jù)集名稱(chēng)
date_source_name = db_data.get("name")
# 轉(zhuǎn) string 取巧,重新定位數(shù)據(jù)集內(nèi)容
string = etree.tostring(db_data, encoding='utf-8').decode('utf-8')
data_source = etree.XML(string)
# 即使 XPATH 定位到的是單個(gè)對(duì)象,但是返回值是一個(gè)list,需要將 list 里的對(duì)象 重新拼成 string
conn = data_source.xpath("/TableData/Connection/DatabaseName/text()")
conn_collect = []
for j in conn:
# 數(shù)據(jù)鏈接名字
conn_collect.append(str(j).strip())
conn_string = "".join(conn_collect)
query = data_source.xpath("/TableData/Query/text()")
query_collect = []
for j in query:
# 數(shù)據(jù)集SQL
query_collect.append(str(j).strip())
query_string = "".join(query_collect)
# 將 解析結(jié)果 拼接到 dict
data_source_json = {
"report": pathlib.Path.relative_to(abs_path, work_directory).__str__(),
"data_source": date_source_name,
"conn": conn_string,
"query": query_string
}
print(json.dumps(data_source_json, ensure_ascii=False))
運(yùn)行結(jié)果
{
"report": "01_dev\\record_monitor\\record_scheduler.cpt",
"data_source": "log_record_execute",
"conn": "LogDB",
"query": "select * from fine_record_execute where todate(time) >= '${start_date}' and todate(time) <= '${end_date}'"
}
可以看到,報(bào)表模板路徑,數(shù)據(jù)集名稱(chēng),數(shù)據(jù)鏈接名稱(chēng),SQL語(yǔ)句都完整獲取到了。
完整代碼
最后將獲取到的內(nèi)容轉(zhuǎn)成 pandas 的 DataFrame,然后輸出到Excel
import pathlib
import pandas as pd
from lxml import etree
# 請(qǐng)先下載 正式環(huán)境reports文件夾,在本地進(jìn)行匹配
work_directory = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets")
top = pathlib.Path("D:\\FineReport_11\\webapps\\webroot\\WEB-INF\\reportlets\\01_dev\\record_monitor")
# 解析結(jié)果 收集
result_list = []
# fineReport .cpt .frm 本身是xml文件,故可直接按行匹配字符查找是否與對(duì)象有關(guān)
# 遞歸獲取 工作目錄 本路徑和子路徑所有擴(kuò)展名為 .cpt 的 文件
for f in top.rglob("*.cpt"):
# 模板文件的絕對(duì)路徑
abs_path = f.absolute()
with open(abs_path, mode="rb") as fr:
xslt_content = fr.read()
# 開(kāi)始解析 模板文件 XML
xml_root = etree.XML(xslt_content)
# 使用 XPATH 定位 XML 中 數(shù)據(jù)集內(nèi)容
tabel_data_el = xml_root.xpath("http://*/TableDataMap//TableData[@class='com.fr.data.impl.DBTableData']")
for db_data in tabel_data_el:
# 數(shù)據(jù)集名稱(chēng)
date_source_name = db_data.get("name")
# 轉(zhuǎn) string 取巧,重新定位數(shù)據(jù)集內(nèi)容
string = etree.tostring(db_data, encoding='utf-8').decode('utf-8')
data_source = etree.XML(string)
# 即使 XPATH 定位到的是單個(gè)對(duì)象,但是返回值是一個(gè)list,需要將 list 里的對(duì)象 重新拼成 string
conn = data_source.xpath("/TableData/Connection/DatabaseName/text()")
conn_collect = []
for j in conn:
# 數(shù)據(jù)鏈接名字
conn_collect.append(str(j).strip())
conn_string = "".join(conn_collect)
query = data_source.xpath("/TableData/Query/text()")
query_collect = []
for j in query:
# 數(shù)據(jù)集SQL
query_collect.append(str(j).strip())
query_string = "".join(query_collect)
# 將 解析結(jié)果 拼接到 dict
data_source_json = {
"report": pathlib.Path.relative_to(abs_path, work_directory).__str__(),
"data_source": date_source_name,
"conn": conn_string,
"query": query_string
}
result_list.append(data_source_json)
df = pd.DataFrame(result_list)
df.to_excel("./data_source.xlsx", engine="openpyxl", index=False)
運(yùn)行結(jié)果

到此這篇關(guān)于使用Python解析FineReport模板數(shù)據(jù)集的文章就介紹到這了,更多相關(guān)Python解析FineReport數(shù)據(jù)集內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Django微信小程序后臺(tái)開(kāi)發(fā)教程的實(shí)現(xiàn)
這篇文章主要介紹了Django微信小程序后臺(tái)開(kāi)發(fā)教程的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
基于python-opencv3實(shí)現(xiàn)圖像顯示和保存操作
這篇文章主要介紹了基于python opencv3的圖像顯示和保存操作方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-06-06
淺談PyQt5中異步刷新UI和Python多線程總結(jié)
今天小編就為大家分享一篇淺談PyQt5中異步刷新UI和Python多線程總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12
pandas讀取文件夾下所有excel文件的實(shí)現(xiàn)
最近需要做一個(gè)需求,要求匯總一個(gè)文件夾所有的excel文件,所以本文就來(lái)介紹一下pandas讀取文件夾下所有excel文件的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
一文詳解Python中的日期計(jì)算和自動(dòng)化運(yùn)行腳本
這篇文章主要為大家詳細(xì)介紹了Python中的日期計(jì)算和自動(dòng)化運(yùn)行腳本的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以了解下2026-03-03
Python數(shù)據(jù)類(lèi)型之Tuple元組實(shí)例詳解
這篇文章主要介紹了Python數(shù)據(jù)類(lèi)型之Tuple元組,結(jié)合實(shí)例形式分析了Python元組類(lèi)型的概念、定義、讀取、連接、判斷等常見(jiàn)操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2019-05-05

