基于配置使用递归调用动态生成多阶报文格式

一、背景:

        如果存在由于不同的业务,不同业务的API接口的报文格式要求不一致,层级和字段名称同样存在多样的需求,每个业务都针对性去开发设计报文生成的代码,重复且工作量大,效率低下。这个时候就需要思考是否可以实现一个可配置,然后根据配置去动态生成想要的报文格式。

二、假设场景:

假设想要生成一个下面的报文格式数据:

{
    "reqData": [
        {
            "A1": "A1",
            "A2": "A2",
            "A3": "A3",
            "A_Line": [
                {
                    "B1": "B11",
                    "B2": "B22",
                    "B3": "B31",
                    "B_Line": [
                        {
                            "C1": "C1",
                            "C2": "C2001",
                            "C_Line": [
                                {
                                    "D1": "153",
                                    "D2": "003"
                                }
                            ]
                        },
                        {
                            "C1": "DC1N",
                            "C2": "C2002",
                            "C_Line": []
                        }
                    ]
                },
                {
                    "B1": "2B1",
                    "B2": "2B2",
                    "B3": "1B3",
                    "B_Line": [
                        {
                            "C1": "DC1",
                            "C2": "003",
                            "C_Line": []
                        },
                        {
                            "C1": "C1N",
                            "C2": "004",
                            "C_Line": []
                        }
                    ]
                },
                {
                    "B1": "4B1",
                    "B2": "B21",
                    "B3": "B31",
                    "B_Line": [
                        {
                            "C1": "",
                            "C2": "005",
                            "C_Line": []
                        }
                    ]
                }
            ]
        },
        {
            "A1": "A11",
            "A2": "A22",
            "A3": "A33",
            "A_Line": [
                {
                    "B1": "5B1",
                    "B2": "5B2",
                    "B3": "1",
                    "B_Line": [
                        {
                            "C1": "C1N",
                            "C2": "0066",
                            "C_Line": [
                                {
                                    "D1": "DD1",
                                    "D2": "D1001"
                                },
                                {
                                    "D1": "D",
                                    "D2": "002"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

 报文reqData节点下是多阶层的数组,A层下面挂B层,B层下面挂着C层,存在多笔数据,每笔数据都有关联关系

三、设计实现:

1、如何设计报文层级(A、B、C)和层级名称(A_Line、B_Line、C_Line)可配置

1.1 配置信息一般配置在数据库DB中,由此设计一个表,可根据业务名称或者id获取报文层级配置信息:

CREATE TABLE LayerSetting(
ID INT NOT NULL,
LayerNo INT NOT NULL,
LayerName VARCHAR(100) NOT NULL,
CommonQueryID VARCHAR(100) NOT NULL
)

 LayerNo :层数
LayerName :层级的名称
CommonQueryID :层级之间关联的查询字段

1.2 新建一个实体类,与DB的配置表一致

public class LayerSettingEntity
{
    public int LayerNo { get; set; }
    public string LayerName { get; set; }
    public string CommonQueryID { get; set; }
}
2、如何设计根据配置和数据动态生成报文

2.1 假设业务数据是从DB获取,则需要按照配置了报文几个层级,就返回几个table的数据集,而且table的字段名称需要与报文要求的字段一致(需要将表字段转成报文字段)

2.2 需要动态生成报文样式,这里涉及实现的思想,采用了递归调用,通过递归调用逐层凭拼接封装报文样式,新增方法AutoFormatGenerated,详细思路看代码注释

public class AutoLayerFromat
{
    /// <summary>
    /// 使用配置生成封装报文样式
    /// </summary>
    /// <param name="rawData">数据集</param>
    /// <param name="sysJobLayerSetting">层级配置信息</param>
    /// <returns></returns>
    public static RequestDataFrom AutoFormatGenerated(DataSet rawData, List<LayerSettingEntity> sysJobLayerSetting)
    {
        if (rawData == null || rawData.Tables.Count == 0 || sysJobLayerSetting == null || sysJobLayerSetting.Count == 0)
            return null;

        var layerSettings = sysJobLayerSetting.OrderBy(x => x.LayerNo).ToList();
        //获取需要屏蔽的字段:比如HeaderID、LineID,这些只是不同阶层的关联查询条件,不需要在报文中呈现
        var forbidKeyList = layerSettings.Select(s => s.CommonQueryID).ToList();

        var dic_header = new Dictionary<string, object>();
        //递归调用:从索引值为0开始
        ProcessLayer(rawData, dic_header, layerSettings, forbidKeyList, 0);
        //排查第一次层的名称reqData(new LayerSettingEntity { LayerNo = 1, LayerName = "reqData",CommonQueryID = "HeaderID" })因为第一层使用了RequestDataFrom实体类字段接收
        var layname = layerSettings[0].LayerName;
        var data = (List<Dictionary<string, object>>)dic_header[layname];


        if (data != null)
        {
            RequestDataFrom req = new RequestDataFrom()
            {
                reqData = data
            };
            return req;
        }
        return null;
    }
    /// <summary>
    /// 递归方法:生成拼接报文
    /// </summary>
    /// <param name="rawData">数据集</param>
    /// <param name="parentDic">报文数组</param>
    /// <param name="layerSettings">层级配置信息</param>
    /// <param name="forbidKeyList">屏蔽字段</param>
    /// <param name="currentLayerNo">当前层级</param>
    /// <param name="filterExpression">查询表达式</param>
    private static void ProcessLayer(DataSet rawData, Dictionary<string, object> parentDic, List<LayerSettingEntity> layerSettings, List<string> forbidKeyList, int currentLayerNo, string filterExpression = "")
    {
        //判断层级信息,根据层数循环
        if (currentLayerNo >= layerSettings.Count)
            return;

        var currentLayerSetting = layerSettings[currentLayerNo];
        var currentTable = rawData.Tables[currentLayerNo];
        var childList = new List<Dictionary<string, object>>();
        
        //使用查询表达式,查询相互关联的阶层的数据
        var childRows = currentTable.Select(filterExpression);
        
        foreach (var childRow in childRows)
        {
            //组合查询条件成查询表达式
            string queryStr = string.Empty;
            for (int i = 1; i<= currentLayerNo+1; i++)
            {
                string querykey = layerSettings.Where(x => x.LayerNo==i).ToList().First().CommonQueryID;
                if (string.IsNullOrEmpty(queryStr))
                {
                    queryStr = $"{querykey}='{childRow[querykey]}'";
                }
                else
                {
                    queryStr += $" AND {querykey}='{childRow[querykey]}'";
                }
            }
            //将行数据转成Dictionary<string, object>的键值对
            var childDic = CreateDictionaryFromDataRow(childRow, forbidKeyList);
            //递归调用
            ProcessLayer(rawData, childDic, layerSettings, forbidKeyList, currentLayerNo + 1, queryStr);

            childList.Add(childDic);
        }
        //拼接数组数据到对应的层级名称下
        parentDic[currentLayerSetting.LayerName] = childList;
    }
    /// <summary>
    /// 将table行数据转成字典
    /// </summary>
    /// <param name="row"></param>
    /// <param name="forbidKeyList"></param>
    /// <returns></returns>
    private static Dictionary<string, object> CreateDictionaryFromDataRow(DataRow row, List<string> forbidKeyList)
    {
        var dic = new Dictionary<string, object>();
        foreach (DataColumn column in row.Table.Columns)
        {
            if (!forbidKeyList.Contains(column.ColumnName))//排除需要屏蔽的字段
            {
                dic[column.ColumnName] = row[column] ?? "";
            }
        }
        return dic;
    }
}
public class RequestDataFrom 
{
    public object reqData { get; set; }
}

四、模拟数据调试

1、模拟数据集,根据假设的场景,需要展开4层的报文结构,所以模拟数据集返回了4个DataTable数据
// 模拟数据集
DataSet rawData = new DataSet();

// 表1
DataTable table1 = new DataTable();
table1.Columns.Add("HeaderID");
table1.Columns.Add("A1");
table1.Columns.Add("A2");
table1.Columns.Add("A3");

DataRow row1 = table1.NewRow();
row1["HeaderID"] = "1";
row1["A1"] = "A1";
row1["A2"] = "A2";
row1["A3"] = "A3";
table1.Rows.Add(row1);

DataRow row2 = table1.NewRow();
row2["HeaderID"] = "2";
row2["A1"] = "A11";
row2["A2"] = "A22";
row2["A3"] = "A33";
table1.Rows.Add(row2);

rawData.Tables.Add(table1);

// 表2
DataTable table2 = new DataTable();
table2.Columns.Add("HeaderID");
table2.Columns.Add("LineID");
table2.Columns.Add("B1");
table2.Columns.Add("B2");
table2.Columns.Add("B3");

DataRow row3 = table2.NewRow();
row3["HeaderID"] = "1";
row3["LineID"] = "1";
row3["B1"] = "B11";
row3["B2"] = "B22";
row3["B3"] = "B31";
table2.Rows.Add(row3);

DataRow row4 = table2.NewRow();
row4["HeaderID"] = "1";
row4["LineID"] = "2";
row4["B1"] = "2B1";
row4["B2"] = "2B2";
row4["B3"] = "1B3";
table2.Rows.Add(row4);

DataRow row5 = table2.NewRow();
row5["HeaderID"] = "1";
row5["LineID"] = "3";
row5["B1"] = "4B1";
row5["B2"] = "B21";
row5["B3"] = "B31";
table2.Rows.Add(row5);

DataRow row6 = table2.NewRow();
row6["HeaderID"] = "2";
row6["LineID"] = "1";
row6["B1"] = "5B1";
row6["B2"] = "5B2";
row6["B3"] = "1";
table2.Rows.Add(row6);

rawData.Tables.Add(table2);

// 表3
DataTable table3 = new DataTable();
table3.Columns.Add("HeaderID");
table3.Columns.Add("LineID");
table3.Columns.Add("SublineID");
table3.Columns.Add("C1");
table3.Columns.Add("C2");

DataRow row7 = table3.NewRow();
row7["HeaderID"] = "1";
row7["LineID"] = "1";
row7["SublineID"] = "1";
row7["C1"] = "C1";
row7["C2"] = "C2001";
table3.Rows.Add(row7);

DataRow row8 = table3.NewRow();
row8["HeaderID"] = "1";
row8["LineID"] = "1";
row8["SublineID"] = "2";
row8["C1"] = "DC1N";
row8["C2"] = "C2002";
table3.Rows.Add(row8);

DataRow row9 = table3.NewRow();
row9["HeaderID"] = "1";
row9["LineID"] = "2";
row9["SublineID"] = "1";
row9["C1"] = "DC1";
row9["C2"] = "003";
table3.Rows.Add(row9);

DataRow row10 = table3.NewRow();
row10["HeaderID"] = "1";
row10["LineID"] = "2";
row10["SublineID"] = "2";
row10["C1"] = "C1N";
row10["C2"] = "004";
table3.Rows.Add(row10);

DataRow row11 = table3.NewRow();
row11["HeaderID"] = "1";
row11["LineID"] = "3";
row11["SublineID"] = "1";
row11["C1"] = "";
row11["C2"] = "005";
table3.Rows.Add(row11);

DataRow row12 = table3.NewRow();
row12["HeaderID"] = "2";
row12["LineID"] = "1";
row12["SublineID"] = "1";
row12["C1"] = "C1N";
row12["C2"] = "0066";
table3.Rows.Add(row12);

rawData.Tables.Add(table3);

//表4
DataTable table4 = new DataTable();
table4.Columns.Add("HeaderID");
table4.Columns.Add("LineID");
table4.Columns.Add("SublineID");
table4.Columns.Add("ExtralineID");
table4.Columns.Add("D1");
table4.Columns.Add("D2");

DataRow row13 = table4.NewRow();
row13["HeaderID"] = "2";
row13["LineID"] = "1";
row13["SublineID"] = "1";
row13["ExtralineID"] = "1";
row13["D1"] = "DD1";
row13["D2"] = "D1001";
table4.Rows.Add(row13);

DataRow row14 = table4.NewRow();
row14["HeaderID"] = "2";
row14["LineID"] = "1";
row14["SublineID"] = "1";
row14["ExtralineID"] = "2";
row14["D1"] = "D";
row14["D2"] = "002";
table4.Rows.Add(row14);

DataRow row15 = table4.NewRow();
row15["HeaderID"] = "1";
row15["LineID"] = "1";
row15["SublineID"] = "1";
row15["ExtralineID"] = "1";
row15["D1"] = "153";
row15["D2"] = "003";
table4.Rows.Add(row15);

rawData.Tables.Add(table4);
2、模拟配置的层级信息
// 配置层级字段名
List<LayerSettingEntity> LayerSetting = new List<LayerSettingEntity>
{
    new LayerSettingEntity { LayerNo = 1, LayerName = "ESBREQUEST",CommonQueryID = "HeaderID" },
    new LayerSettingEntity { LayerNo = 2, LayerName = "A_Line", CommonQueryID = "LineID" },
    new LayerSettingEntity { LayerNo = 3, LayerName = "B_Line", CommonQueryID = "SublineID"},
    new LayerSettingEntity { LayerNo = 4, LayerName = "C_Line", CommonQueryID = "ExtralineID" }
};
3、测试调用

图片展示不完整,实现输出的报文与需求的报文格式一致。 

五、多种不同的报文层级和名称场景测试

1、一层的使用不到配置,故不作测试
2、两层测试,模拟数据
// 模拟数据集
DataSet rawData = new DataSet();

// 表1
DataTable table1 = new DataTable();
table1.Columns.Add("HeaderID");
table1.Columns.Add("A1");
table1.Columns.Add("A2");
table1.Columns.Add("A3");

DataRow row1 = table1.NewRow();
row1["HeaderID"] = "1";
row1["A1"] = "A1";
row1["A2"] = "A2";
row1["A3"] = "A3";
table1.Rows.Add(row1);

DataRow row2 = table1.NewRow();
row2["HeaderID"] = "2";
row2["A1"] = "A11";
row2["A2"] = "A22";
row2["A3"] = "A33";
table1.Rows.Add(row2);

rawData.Tables.Add(table1);

// 表2
DataTable table2 = new DataTable();
table2.Columns.Add("HeaderID");
table2.Columns.Add("LineID");
table2.Columns.Add("B1");
table2.Columns.Add("B2");
table2.Columns.Add("B3");

DataRow row3 = table2.NewRow();
row3["HeaderID"] = "1";
row3["LineID"] = "1";
row3["B1"] = "B11";
row3["B2"] = "B22";
row3["B3"] = "B31";
table2.Rows.Add(row3);

DataRow row4 = table2.NewRow();
row4["HeaderID"] = "1";
row4["LineID"] = "2";
row4["B1"] = "2B1";
row4["B2"] = "2B2";
row4["B3"] = "1B3";
table2.Rows.Add(row4);

DataRow row5 = table2.NewRow();
row5["HeaderID"] = "1";
row5["LineID"] = "3";
row5["B1"] = "4B1";
row5["B2"] = "B21";
row5["B3"] = "B31";
table2.Rows.Add(row5);

DataRow row6 = table2.NewRow();
row6["HeaderID"] = "2";
row6["LineID"] = "1";
row6["B1"] = "5B1";
row6["B2"] = "5B2";
row6["B3"] = "1";
table2.Rows.Add(row6);

rawData.Tables.Add(table2);
 // 配置层级字段名
 List<LayerSettingEntity> LayerSetting = new List<LayerSettingEntity>
 {
     new LayerSettingEntity { LayerNo = 1, LayerName = "reqData",CommonQueryID = "HeaderID" },
     new LayerSettingEntity { LayerNo = 2, LayerName = "两层_Line", CommonQueryID = "LineID" }
};

3、三层测试 ,模拟数据

// 模拟数据集
DataSet rawData = new DataSet();

// 表1
DataTable table1 = new DataTable();
table1.Columns.Add("HeaderID");
table1.Columns.Add("A1");
table1.Columns.Add("A2");
table1.Columns.Add("A3");

DataRow row1 = table1.NewRow();
row1["HeaderID"] = "1";
row1["A1"] = "A1";
row1["A2"] = "A2";
row1["A3"] = "A3";
table1.Rows.Add(row1);

DataRow row2 = table1.NewRow();
row2["HeaderID"] = "2";
row2["A1"] = "A11";
row2["A2"] = "A22";
row2["A3"] = "A33";
table1.Rows.Add(row2);

rawData.Tables.Add(table1);

// 表2
DataTable table2 = new DataTable();
table2.Columns.Add("HeaderID");
table2.Columns.Add("LineID");
table2.Columns.Add("B1");
table2.Columns.Add("B2");
table2.Columns.Add("B3");

DataRow row3 = table2.NewRow();
row3["HeaderID"] = "1";
row3["LineID"] = "1";
row3["B1"] = "B11";
row3["B2"] = "B22";
row3["B3"] = "B31";
table2.Rows.Add(row3);

DataRow row4 = table2.NewRow();
row4["HeaderID"] = "1";
row4["LineID"] = "2";
row4["B1"] = "2B1";
row4["B2"] = "2B2";
row4["B3"] = "1B3";
table2.Rows.Add(row4);

DataRow row5 = table2.NewRow();
row5["HeaderID"] = "1";
row5["LineID"] = "3";
row5["B1"] = "4B1";
row5["B2"] = "B21";
row5["B3"] = "B31";
table2.Rows.Add(row5);

DataRow row6 = table2.NewRow();
row6["HeaderID"] = "2";
row6["LineID"] = "1";
row6["B1"] = "5B1";
row6["B2"] = "5B2";
row6["B3"] = "1";
table2.Rows.Add(row6);

rawData.Tables.Add(table2);

// 表3
DataTable table3 = new DataTable();
table3.Columns.Add("HeaderID");
table3.Columns.Add("LineID");
table3.Columns.Add("SublineID");
table3.Columns.Add("C1");
table3.Columns.Add("C2");

DataRow row7 = table3.NewRow();
row7["HeaderID"] = "1";
row7["LineID"] = "1";
row7["SublineID"] = "1";
row7["C1"] = "C1";
row7["C2"] = "C2001";
table3.Rows.Add(row7);

DataRow row8 = table3.NewRow();
row8["HeaderID"] = "1";
row8["LineID"] = "1";
row8["SublineID"] = "2";
row8["C1"] = "DC1N";
row8["C2"] = "C2002";
table3.Rows.Add(row8);

DataRow row9 = table3.NewRow();
row9["HeaderID"] = "1";
row9["LineID"] = "2";
row9["SublineID"] = "1";
row9["C1"] = "DC1";
row9["C2"] = "003";
table3.Rows.Add(row9);

DataRow row10 = table3.NewRow();
row10["HeaderID"] = "1";
row10["LineID"] = "2";
row10["SublineID"] = "2";
row10["C1"] = "C1N";
row10["C2"] = "004";
table3.Rows.Add(row10);

DataRow row11 = table3.NewRow();
row11["HeaderID"] = "1";
row11["LineID"] = "3";
row11["SublineID"] = "1";
row11["C1"] = "";
row11["C2"] = "005";
table3.Rows.Add(row11);

DataRow row12 = table3.NewRow();
row12["HeaderID"] = "2";
row12["LineID"] = "1";
row12["SublineID"] = "1";
row12["C1"] = "C1N";
row12["C2"] = "0066";
table3.Rows.Add(row12);

rawData.Tables.Add(table3);
 // 配置层级字段名
 List<LayerSettingEntity> LayerSetting = new List<LayerSettingEntity>
 {
     new LayerSettingEntity { LayerNo = 1, LayerName = "reqData",CommonQueryID = "HeaderID" },
     new LayerSettingEntity { LayerNo = 2, LayerName = "两层_Line", CommonQueryID = "LineID" },
     new LayerSettingEntity { LayerNo = 3, LayerName = "三层_Line", CommonQueryID = "SublineID"}
 };

六、总结

        使用可配置和递归调用,实现了一个公共的方法可以支持动态生成不同业务需求的报文格式,减少的重复代码的开发,提升了开发效率,实现代码优化和提升代码的可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值