反饋已提交
網絡繁忙
企業資料源格式多種多樣,部分資料格式無法直接應用於FineReport中。本插件提供資料格式轉換能力,可對接JSON、WebService、網路爬蟲、自主開發的資料中心等,並將其轉換成FineReport可用的資料類型。同時,該插件具有高度擴展性,也可以透過二次開發來適應更復雜的場景。
注:1.基於此插件進行擴展開發需要具備最基礎的 FineReport 插件開發能力。
2.使用子插件時升級,需要先禁用子插件,或升級後重啟伺服器。
插件透過不同的裝載器和解析器實現個性化的資料裝配,將從外部獲取的資料轉換成 FineReport 可用的資料格式。
點選下載插件:資料工廠資料集
設計器插件安裝方法參照:設計器插件管理
伺服器安裝插件方法參照:伺服器插件管理
1)插件安裝後,資料集管理面板新增資料工廠資料集,如下圖所示:
2)點選進入資料工廠主面板,使用主面板可以應對絕大多數的場景。主面板具體功能介紹如下:
①名稱:在名稱欄可以設定該資料集的名稱。
②裝載器和解析器:裝載器負責從資料源裝載資料,解析器則負責將裝載的資料轉換成 FineReport 支援的資料模型。
插件內建了兩種裝載方式(Http、單參數)和三種解析方式(Json 、Js 解析器、原始資料)。
在下拉框中選擇裝載方式和解析方式,具體的裝載器、解析器配置方法將在下文第3節進行詳細介紹。
③進階設定:當內建裝載器和解析器無法滿足使用者需求時,可以使用進階設定功能進行二次開發,以實現功能擴展。該功能將在下文第4節進行詳細介紹。
④快取:快取的意思是指將外部讀取的資料暫存到快取中,直接從快取中取數,而不透過API取數,以提高資料讀取的速度。快取有 3 個選項:無、磁碟、記憶體。
選擇“無”表示不啟動快取功能
選擇“磁碟”表示快取到本地磁碟
選擇“記憶體”表示快取到記憶體
n分鐘:表示n分鐘內從快取中讀取靜態資料,n分鐘後再次從API重新讀取資料,重新讀取的資料依然在快取中儲存n分鐘。
⑤參數欄:參數欄裏面的參數會在後台被分為兩類:
第一類是裝載器介面出現過的參數( ${參數名} ),該類參數名可被參數面板自動識別,同時解析到裝載器配置的字串裏。 參數文法:用 ${參數名}獲得參數的值,支援正文、公式等多種形式的參數,也支援從報表中讀取參數。
第二類則是裝載器介面未出現過的,使用者可透過參數面板的插入按鈕來新增新的參數作為請求參數。
在參數面板中,“參數”列中記錄的是參數名;“值”列中點選按鈕可以選擇參數資料類型(包括字串、整數型態、雙精度型、日期、布林型、公式),可以在該列中輸入參數的取值。分別可以實現參數所在行的刪除、上移和下移。
⑥預覽:點選預覽按鈕,可以預覽格式轉換後的資料。
3)完成配置後,在FineReport設計器中,可看到範本資料集中新增的資料工廠資料集,該資料集可直接應用於報表製作。
插件內建了兩種裝載方式,分別為:
Http:透過配置相關請求,實現從 Web 端裝載資料。( 比如 JSON 服務等 )
單參數:可以實現將裝載器介面的資料參數,經過計算後原樣傳給解析器,主要是供除錯使用。( 比如 JSON、XML 等結構化資料 )
當選擇Http裝載方式時,裝載器的內容是一個JSON,裏面有4個欄位,分別是
具體使用範例如表所示:
請求類型
裝載器內容
參數面板
取值
實際請求
備註
{ url:"http://example.com/", type:"GET", charset:"UTF-8", header:{ aa:${bb} }}
參數 值bb p_BBcc p_CC
url: http://example.com/?cc=p_CC
type: get
header:{aa:p_BB}
使用者在參數面板中新增的第二類額外參數會作為query參數連接到url後。
{ url:"http://example.com/", type:"POST", charset:"UTF-8", header:{ aa:${bb} }}
url: http://example.com/
type: post
header: {aa:p_BB}
body:{ cc: p_CC}
使用者在參數面板中新增的第二類額外參數在請求體body裏。body為x-www-form-urlencoded模式,內容為cc=p_CC。
JSON
{ url:"http://example.com/", type:"JSON", charset:"UTF-8", header:{ aa:${bb} }}
參數 值
bb p_BB
header: {aa:p_BB,
content-type: json}
JSON 實際上是post raw模式,第二類參數會被裝載成JSON的格式作為請求體傳送。對於POST請求這種方式使用的更普遍。
body有兩種裝載方式:一種是類似用post的。第二種是直接寫body鍵。
{ url:"http://example.com/", type:"JSON", charset:"UTF-8", header:{ aa:${bb} }, body:{ cc:${dd} }}
dd p_DD
content-type: json}body:{ cc:p_DD}
注:範例中的http://example.com/僅為示範,實際請按照真實API進行調整。
2.5.24版本新增支援GET請求透過BODY傳遞參數(POST請求不適用),具體說明如下:
當請求類型為GET時,提供Content-Type參數及body配合使用
範例
在裝載方式下拉框中選擇“單參數”裝載方式,在裝載器內容框中輸入 ${data}${today()} ,在解析方式下拉框中選擇“原始資料”解析方式(表示原樣傳回資料,將在下文3.2.3進行介紹),在參數面板配置參數如下:
參數 值data myData
點選預覽按鈕,得到如下結果:
插件內建了三種解析方式,分別為:
Json:對結構化的 Json資料進行解析,生成 FineReport 支援的資料模型。
注:只能解析結構型描述的 JSON,業務型描述的 JSON 無法解析,業務型 JSON 解析依賴具體的業務的描述,需要單獨實現解析器。
Js解析器:透過Js腳本解析資料,生成 FineReport 支援的資料模型。
注:當資料量較大時要謹慎使用。
原始資料:原樣傳回資料,主要是供除錯使用。
提供了不完整的JPATH文法。欄位含義如下:
dataPath:取值預設為root,表示處理全部資料;取值root.key1.key2.arr[1] 表示定位到{ key1 : { key2 : { arr:[ {},{<這裏>} ] }} },這種情況下只解析定位路徑下的json內容。
showmap:提供列名映射,將原始的列名(path1)轉換為自訂的列名(value1)。
參照JSON資料集插件中Js文法,$data變數為接受到的原始資料,最終需要透過Js生成如下一個資料表物件
傳回的物件樣例,column為列名,content為二維陣列,儲存資料集儲存格的值
{ "column":["col1", "col2", "col3"], "content":[ [1, 2, 3], [4, 5, 6] ]}
範例:具體程式碼請按照實際場景開發
裝載器傳回資料如下:
{ "monthFactoryWaitingRateDataList": [ { "factory": "HZC01", "dayFactoryWaitingRateList": [0,0,0], "days": ["2023-04-01","2023-04-02","2023-04-03"], "dayStrs": ["04-01","04-02","04-03"] }, { "factory": "HZC02", "dayFactoryWaitingRateList": [0,0,0], "days": ["2023-04-01","2023-04-02","2023-04-03"], "dayStrs": ["04-01","04-02","04-03"] } ]}
JS解析器填寫範例:
var x =$data;var column = Object.keys(x.monthFactoryWaitingRateDataList[0]);var content = [];for (var i = 0; i < x.monthFactoryWaitingRateDataList.length; i++) { for (var j = 0; j < x.monthFactoryWaitingRateDataList[i].dayFactoryWaitingRateList.length; j++) { var contentLine = []; contentLine.push(x.monthFactoryWaitingRateDataList[i].factory, x.monthFactoryWaitingRateDataList[i].dayFactoryWaitingRateList[j], x.monthFactoryWaitingRateDataList[i].days[j], x.monthFactoryWaitingRateDataList[i].dayStrs[j]); content.push(contentLine); }}var result = {};result.column = column;result.content = content;return result;
預覽資料,效果如下:
Js解析器效能比較低,資料量大的時候要慎用。
插件2.5.13版本Js解析器開始使用JDK內建Nashorn引擎,預設支援ES5文法,高版本文法暫不支援。
類比單參數裝載器,傳回一個只有一個儲存格的資料集,資料集的內容是從裝載器接受到的原始資料,參數面板無意義。具體範例參照3.1.2。
獲取帆軟市場的插件列表
裝載方式:Http
選擇Http裝載方式以實現從 Web 端裝載資料。
{ url:"https://market.fanruan.com/plugins/commodity?cid=&fee=&seller=&version=&language=&searchKeyword=&_=1650781600900", type:"GET", charset:"UTF-8", header:{}}
注:1. 具體的裝載器邏輯可以單獨客制,並沒有什麼統一的寫法規範
2. API實際傳回Json格式的資料如下(此處做對比說明,實際插件使用程式中,不會出現該步驟)
解析方式 :Json
選擇Json解析方式以對結構化的 Json資料進行解析。
{ dataPath:"root.result", showmap:"path1,value1,path2,value2",}
即主面板配置如下:
點選預覽按鈕,傳回經格式轉換後的資料結果如下:
假設有如下API用來獲取某城市天氣 http://example.com?city=北京
如果我們希望根據範本元件內容動態獲取城市的資料,可以在範本中設定一個元件 city
然後在資料工廠面板中進行如下配置:
方式一:在裝載器中新增同名參數 參數預設值置空
{ url:"http://example.com?city=${city}", type:"GET", charset:"UTF-8", header:{}}
方式二:在參數面板中手動新增參數city,參數值置空
{ url:"http://example.com", type:"GET", charset:"UTF-8", header:{}}
假設API回傳值如下:
{ result:[ { district:海淀區, temperature:20, }, { district:朝陽區, temperature:23, } ]}
解析方式:Json
{ dataPath:"root.result", showmap:"district,轄區,temperature,溫度"}
使用主面板的進階設定功能需要有一定的插件開發能力。
假如使用者認為內建的裝載方式、解析方式不足以滿足使用場景,又不想重新開發一個裝載器/解析器時,可以點選資料工廠主面板的“進階設定”按鈕自訂輔助步驟。
進階設定面板的作用是用來新增裝載前、裝載後、解析後事件作為裝載方式、解析方式的補足,支援多個同步驟事件的新增。
在“進階設定”面板最上層下拉框中可以選擇“為該資料集單獨設定”/“使用範本設定”/“使用全局設定”。
選擇“為該資料集單獨設定”時下方會出現列表,支援事件的增刪改。
選擇“使用範本設定”/“使用全局設定”時下方不顯示列表,具體設定項需開啟右側“全局設定”/“範本設定”進行編輯。
目前各個事件並沒有實裝具體的應用,僅提供了接入框架,進階步驟是裝載-解析模式的增強和補全,結合開發篇來使用。
提供額外的參數引入 比如說token獲取的邏輯可以寫在自訂裝載前事件裏。多個裝載前事件參數可以累積,重複的參數前者會被後者改寫。
在主面板使用,使用方法同標準參數呼叫,右側參數欄內預設值無效。
場景範例: 獲取token,可以開發一個token獲取的裝載前事件,將獲取的token作為參數引入主邏輯中。
裝載前事件preprocessorAPI描述:
package com.tptj.plugin.hg.fun; import com.fr.script.Calculator;import com.fr.stable.ParameterProvider; /** * 預處理階段 */public interface Preprocessor extends Configuration { String XML_TAG = "TableDataPreprocessor"; int CURRENT_LEVEL = 1; /** * 預處理 可以改變參數 * @param calculator 算子 * @param config 面板配置內容 * @return 注入的參數 * @throws Exception */ ParameterProvider[] process(Calculator calculator, String config) throws Exception;}
裝載後事件
提供裝載器獲取的原始資料的整理 通常裝載器傳回的是一個字串類型,可以在這個階段進行額外的整理行為,多個事件從上到下順序執行。
場景範例:傳回的資料雖然是json但不適合直接用json解析器,用js解析器效能又不夠好的時候,可以客製一個裝載後事件,把結構格式化為json解析器能夠識別的樣式。
裝載後事件formatterAPI描述:
package com.tptj.plugin.hg.fun; public interface Formatter extends Configuration{ String XML_TAG = "TableDataFormatter"; int CURRENT_LEVEL = 1; /** * 資料格式化 * @param data 裝載器傳回的資料 * @param config 配置 * @return 格式化後的資料 * @throws Exception */ Object format(Object data, String config) throws Exception;}
解析後事件
提供解析器傳回的資料集的進一步處理 比如增刪列,修改列名,重排序,條件過濾等,可以在這個階段進行額外的整理行為,多個事件從上到下順序執行。
場景範例: 排序,篩選,取前n項,取奇數項,調整資料格式等等自由運算元據集內容。
解析後事件filterAPI描述:
package com.tptj.plugin.hg.fun; import com.tptj.plugin.hg.stable.SimpleDataModel; /** * */public interface Filter extends Configuration { String XML_TAG = "TableDataFilter"; int CURRENT_LEVEL = 1; /** * 整理資料 * @param dataModel 資料模型 * @param config 配置 * @return 整理後的資料 * @throws Exception */ SimpleDataModel doAction(SimpleDataModel dataModel, String config) throws Exception; }
package com.tptj.plugin.hg.fun; import com.fr.script.Calculator;import com.fr.stable.ParameterProvider;import com.fr.stable.fun.mark.Mutable; public interface Loader extends Mutable { String XML_TAG = "TableDataLoader"; int CURRENT_LEVEL = 1; /** * 裝載資料 * @param cal 當前算子 * @param params 需要用到的參數 * @param others 其他你可能用到的但是又不希望透過參數控制的配置項,即裝載器面板內容 * @return 儲存資料的物件 */ Object load( Calculator cal, ParameterProvider[] params, String others); /** * 裝載器的名稱【顯示在資料工廠配置介面的下拉列表裏面的】,唯一,支援國際化的key * @return */ String getName(); /** * * @return 預設顯示的配置【顯示在資料工廠配置介面裝載器配置正文域裏面的,配置項自訂,其實就是放一些,不想寫死在程式碼裏,又不希望被參數篡改的部分】 */ String getDefaultConfig();}
package com.tptj.plugin.hg.fun; import com.fr.stable.fun.mark.Mutable;import com.tptj.plugin.hg.stable.SimpleDataModel; public interface Resolver extends Mutable { String XML_TAG = "TableDataResolver"; int CURRENT_LEVEL = 1; /** * 解析資料 * @param data 從裝載器得到的原始資料 * @param others 解析時會使用,但是不希望是參數的配置,即解析器面板內容 * @return 解析出來的二維資料模型 */ SimpleDataModel parse(Object data, String others ); /** * 解析器的名稱【顯示在資料工廠配置介面的下拉列表裏面的】,唯一,支援國際化的key * @return */ String getName(); /** * * @return 預設顯示的配置【顯示在資料工廠配置介面解析器配置正文域裏面的,配置項自訂,其實就是放一些,不想寫死在程式碼裏,又不希望被參數篡改的部分】 */ String getDefaultConfig(); }
<extra-core> <TableDataLoader class="com.fr.plugin.tptj.tabledata.factory.demo.DemoLoader"/> <TableDataResolver class="com.fr.plugin.tptj.tabledata.factory.demo.DemoResolver"/> <TableDataPreprocessor class="com.fr.plugin.tptj.tabledata.factory.demo.DemoPreprocessor"/> <TableDataFormatter class="com.fr.plugin.tptj.tabledata.factory.demo.DemoFormatter"/> <TableDataFilter class="com.fr.plugin.tptj.tabledata.factory.demo.DemoFilter"/> </extra-core><dependence> <Item key="com.tptj.plugin.hg.tabledata.factory.v10" type="plugin"/> <!-- 依賴於資料工廠主框架 --> </dependence>
我們通常繼承抽象類來開發
package com.fr.plugin.tptj.tabledata.factory.demo; import com.fr.base.Parameter;import com.fr.json.JSONObject;import com.fr.script.Calculator;import com.fr.stable.ParameterProvider;import com.tptj.plugin.hg.impl.AbstractPreprocessor; public class DemoPreprocessor extends AbstractPreprocessor { @Override public ParameterProvider[] process(Calculator calculator, String s) { JSONObject jo = new JSONObject(s); ParameterProvider[] results = new ParameterProvider[1]; results[0] = new Parameter("token", jo.get("token")); //jo.get("token"); return results; } @Override public String getName() { return "Demo_PreProcess"; } @Override public String getDefaultConfig() { return "{\n" + " token:token\n" + "}"; }}
package com.fr.plugin.tptj.tabledata.factory.demo; import com.tptj.plugin.hg.impl.AbstractFormatter; public class DemoFormatter extends AbstractFormatter { @Override public Object format(Object o, String s) { String data = (String) o; String[] lines = data.split("\n"); StringBuilder result = new StringBuilder(); for (int i = 1; i < lines.length; i++) { result.append(lines[i]).append("\n"); } return result.toString(); } @Override public String getName() { return "Demo_Formatter"; } @Override public String getDefaultConfig() { return "remove first line"; }}
package com.fr.plugin.tptj.tabledata.factory.demo; import com.fr.general.data.TableDataException;import com.fr.log.FineLoggerFactory;import com.tptj.plugin.hg.impl.AbstractFilter;import com.tptj.plugin.hg.stable.SimpleDataModel; import java.util.ArrayList;import java.util.List; public class DemoFilter extends AbstractFilter { @Override public SimpleDataModel doAction(SimpleDataModel simpleDataModel, String s) { try { simpleDataModel.removeColumn(simpleDataModel.getColumnCount() - 1); List<Object[]> newData = new ArrayList<Object[]>(); for(Object[] row : simpleDataModel.getDatas()) { Object[] newRow = new Object[row.length - 1]; System.arraycopy(row, 0, newRow, 0, row.length - 1); newData.add(newRow); } simpleDataModel.setDatas(newData); //simpleDataModel.getDatas().remove(simpleDataModel.getColumnCount() - 1); } catch (TableDataException e) { FineLoggerFactory.getLogger().error(e, "{}", e.getMessage()); } return simpleDataModel; } @Override public String getName() { return "Demo_Filter"; } @Override public String getDefaultConfig() { return "remove last col"; }}
package com.tptj.plugin.hg.tabledata.factory; import com.tptj.plugin.hg.impl.AbstractResolver;import com.tptj.plugin.hg.stable.SimpleDataModel; public class DemoResolver extends AbstractResolver { @Override public SimpleDataModel parse(Object jsonStr, String others) { return new SimpleDataModel(); } public static final String KEY = "Demo"; @Override public String getName() { return KEY; } @Override public String getDefaultConfig() { return "Is a demo"; } }
package com.tptj.plugin.hg.tabledata.factory.core.loader; import com.fr.json.JSONObject;import com.fr.script.Calculator;import com.fr.stable.ParameterProvider;import com.tptj.plugin.hg.impl.AbstractLoader; public class SingleParamLoader extends AbstractLoader { @Override public Object load( Calculator calculator, ParameterProvider[] parameters, String others ) { try{ JSONObject obj = new JSONObject(others); if( obj.has("data") ){ return obj.optString("data", "{}"); } }catch(Exception e){ e.printStackTrace(); } return ""; } public static final String KEY = "Plugin-Factory_Data_Set_Loader_Type_Single_Parameter"; @Override public String getName() { return KEY; } @Override public String getDefaultConfig() { StringBuilder sb = new StringBuilder(); sb.append("{\r\n") .append(" data:\"待解析的json字串,支援${p1}這樣的參數和公式\",\r\n") .append("}\r\n"); return sb.toString(); } }
SimpleDataModel繼承的標準資料集格式。負責儲存資料的結構為一個二維表datas和一個一維表cols,分別是具體內容和列名。呼叫對應的set,add方法即可向資料集寫入資料。自訂面板API,很多時候,預設正文框對於配置來說過於簡陋了。如果想要更豐富的UI可以使用這個API更換默認的正文框。
package com.tptj.plugin.hg.fun; import com.fr.stable.fun.mark.Mutable; import javax.swing.*;import java.awt.event.FocusListener; public interface ConfigTable<T extends Configuration> extends Mutable { String XML_TAG = "TableDataConfigTable"; int CURRENT_LEVEL = 1; String getName(); /** * 寫入資料到JPanal * @param config */ void setValue(String config); /** * 讀取資料到 * @return */ String getValue(); JPanel getTable(FocusListener listener);}
使用時繼承抽象類
package com.tptj.plugin.hg.fun;import com.fr.stable.fun.mark.Mutable;import javax.swing.*;import java.awt.event.FocusListener;/** * 需要泛型繼承對應的Configuration進行綁定 * @param <T> */public interface ConfigTable<T extends Configuration> extends Mutable { String XML_TAG = "TableDataConfigTable"; int CURRENT_LEVEL = 1; String getName(); /** * 寫入資料到JPanal * @param config */ void setValue(String config); /** * 讀取資料到 * @return */ String getValue(); /** * 獲取配置面板 * @deprecated use {@link ConfigTable#getTable(ParameterRefreshAction action)} instead * FocusListener觸發條件有限,不足以滿足所有的需求,建議使用ParameterRefreshAction手動綁定事件 * @param listener FocusListener 事件回呼 * @return */ JPanel getTable(FocusListener listener); /** * 獲取配置面板 * @param action 透過{@link ParameterRefreshAction#doAction()} 進行獲取重新整理事件,自己綁定 * @return */ JPanel getTable(ParameterRefreshAction action);}
功能點註冊<extra-designer> <TableDataConfigTable class="com.tptj.plugin.hg.tabledata.factory.core.filter.DefaultFilterTextTable"/> </extra-designer>
完整開發demo見 資料工廠二次開發demo範例
FR10.0插件開發 - 插件文檔
遇到模版載入時間長的問題時,如果懷疑是資料工廠資料集執行慢的話,可以透過分析日誌來分析資料工廠資料集的執行時間,包括(裝載器執行時間,解析器執行時間,單個資料工廠資料集的總執行時間)
測試步驟:
日誌調整成debug級別,預覽模版或者資料集,在fanruan.log中搜尋如下日誌:1)table data factory eval start ... 含義:---開始執行資料工廠資料集2)start loading data,config is 含義:----開始執行裝載器3)loading data end,cost 含義:---裝載器執行結束4)start json resolving data ... 含義:--開始執行JSON解析器5)resolving data end,cost 含義:--解析器執行結束6)table data factory eval end,cost 含義:–資料工廠執行結束
日誌中可見裝載器、解析器、資料工廠資料集整體的取數耗時,若裝載器執行時間較長時,一般為呼叫的API回应時間過長,可透過其他方式呼叫API查看回应時間進行問題定位
插件版本要求:2.5.21及以上。
滑鼠選中內容,快速回饋問題
滑鼠選中存在疑惑的內容,即可快速回饋問題,我們將會跟進處理。
不再提示
10s後關閉
反馈已提交
网络繁忙