基于Karate实现带主键的大规模JSON文件差异对比方案问询
问题
我有两个需对比的大规模JSON文件(示例如下),需求如下:
- 以
S_Object_ID为主键,按条目单独获取差异; - 忽略条目顺序,得到对比结果;
- 验证子条目块的主键
Sequence_Number; - 找出两个文件中各自缺失的条目。
请问在Karate中实现该需求的最优方式是什么?
JSON FILE-1
{ "Items": [ { "Delivery_Item": "Y", "S_Object_ID": "1002", "Sweetener_Group": "N/A", "DMB_Item": "N" }, { "Delivery_Item": "Y", "S_Object_ID": "1007", "Sweetener_Group": "N/A", "DMB_Item": "N" }, { "Delivery_Item": "Y", "S_Object_ID": "101000001", "Sweetener_Group": [ { "Min_Qty": "0", "Pricing_Level": "N/A", "Sequence_Number": "1", "LTO": "-", "Default_Quantity_XL": "N/A", "Group": "Sweetener_1", "Default_Quantity_L": "N/A", "S_Object_Item_ID": "-", "Max_Qty": "1", "Regional": "N/A" }, { "Min_Qty": "0", "Pricing_Level": "N/A", "Sequence_Number": "2", "LTO": "-", "Default_Quantity_XL": "5", "Group": "Sweetener_1", "Default_Quantity_L": "4", "S_Object_Item_ID": "102003004", "Max_Qty": "10", "Regional": "5" } ], "DMB_Item": "N" } ], "Count": 3 }
JSON FILE-2
{ "Items": [ { "Delivery_Item": "N", "S_Object_ID": "1007", "Sweetener_Group": "N/A", "DMB_Item": "N" }, { "Delivery_Item": "Y", "S_Object_ID": "1002", "Sweetener_Group": "N/A", "DMB_Item": "N" }, { "Delivery_Item": "Y", "S_Object_ID": "101000001", "Sweetener_Group": [ { "Min_Qty": "5", "Pricing_Level": "N/A", "Sequence_Number": "1", "LTO": "-", "Default_Quantity_XL": "N/A", "Group": "Sweetener_1", "Default_Quantity_L": "N/A", "S_Object_Item_ID": "-", "Max_Qty": "1", "Regional": "N/A" } ], "DMB_Item": "N" }, { "Delivery_Item": "Y", "S_Object_ID": "101000002", "Sweetener_Group": "N/A", "DMB_Item": "N" } ], "Count": 4 }
解决方案
Karate的核心特性(match断言、JSON转换、集合操作)可以完美覆盖这些需求,以下是最优实现步骤:
1. 加载文件并转换为键值映射
先把两个文件的Items数组转成以S_Object_ID为键的Map,直接忽略条目顺序,按主键快速定位条目:
* def file1 = read('file1.json') * def file2 = read('file2.json') * def map1 = karate.mapWithKey(file1.Items, 'S_Object_ID') * def map2 = karate.mapWithKey(file2.Items, 'S_Object_ID')
2. 找出双方缺失的条目
提取两个Map的主键集合,对比得到各自缺失的条目ID:
* def keys1 = karate.keysOf(map1) * def keys2 = karate.keysOf(map2) * def missingInFile1 = karate.filter(keys2, function(x){ return !(x in map1) }) * def missingInFile2 = karate.filter(keys1, function(x){ return !(x in map2) }) * print 'File1缺失的条目ID:', missingInFile1 * print 'File2缺失的条目ID:', missingInFile2
3. 按主键对比条目差异
遍历共同主键,逐一对比顶层字段和子数组Sweetener_Group的差异:
* def commonKeys = karate.filter(keys1, function(x){ return x in map2 }) * def diffs = [] * karate.forEach(commonKeys, function(key){ * def item1 = map1[key] * def item2 = map2[key] * def diff = { S_Object_ID: key } // 对比顶层字段 * if (item1.Delivery_Item != item2.Delivery_Item) diff.Delivery_Item = [item1.Delivery_Item, item2.Delivery_Item] * if (item1.DMB_Item != item2.DMB_Item) diff.DMB_Item = [item1.DMB_Item, item2.DMB_Item] // 处理Sweetener_Group的对比 * def sg1 = item1.Sweetener_Group * def sg2 = item2.Sweetener_Group * if (sg1 != sg2) { * if (sg1 == 'N/A' || sg2 == 'N/A') { * diff.Sweetener_Group = [sg1, sg2] } else { // 子数组按Sequence_Number转Map后对比 * def sgMap1 = karate.mapWithKey(sg1, 'Sequence_Number') * def sgMap2 = karate.mapWithKey(sg2, 'Sequence_Number') * def sgDiff = {} // 子数组缺失的Sequence_Number * def sgKeys1 = karate.keysOf(sgMap1) * def sgKeys2 = karate.keysOf(sgMap2) * def sgMissingInFile1 = karate.filter(sgKeys2, function(x){ return !(x in sgMap1) }) * def sgMissingInFile2 = karate.filter(sgKeys1, function(x){ return !(x in sgMap2) }) * if (sgMissingInFile1.length > 0) sgDiff.missingInFile1 = sgMissingInFile1 * if (sgMissingInFile2.length > 0) sgDiff.missingInFile2 = sgMissingInFile2 // 子数组字段差异 * def sgCommonKeys = karate.filter(sgKeys1, function(x){ return x in sgMap2 }) * karate.forEach(sgCommonKeys, function(sgKey){ * def sgItem1 = sgMap1[sgKey] * def sgItem2 = sgMap2[sgKey] * def sgItemDiff = {} * karate.forEach(karate.keysOf(sgItem1), function(field){ * if (sgItem1[field] != sgItem2[field]) sgItemDiff[field] = [sgItem1[field], sgItem2[field]] }) * if (!karate.isEmpty(sgItemDiff)) sgDiff[sgKey] = sgItemDiff }) * if (!karate.isEmpty(sgDiff)) diff.Sweetener_Group = sgDiff } } * if (!karate.isEmpty(diff)) karate.appendTo(diffs, diff) }) * print '条目差异详情:', diffs
4. 验证子条目Sequence_Number规则
如果需要校验Sweetener_Group的Sequence_Number是否连续递增,可单独编写验证逻辑:
* def validateSequence = function(items){ * karate.forEach(items, function(item){ * def sg = item.Sweetener_Group * if (sg != 'N/A') { * def sequences = sg.map(function(x){ return parseInt(x.Sequence_Number) }) * sequences.sort() * karate.forEach(sequences, function(num, index){ * assert num == index + 1 }) } }) } * validateSequence(file1.Items) * validateSequence(file2.Items)
5. 断言结果(可选)
如果是测试场景,可直接对对比结果做断言验证:
// 断言File1缺失的条目是101000002 * assert missingInFile1 == ['101000002'] // 断言差异列表包含预期条目 * assert diffs.length == 2 * assert diffs[0].S_Object_ID == '1007' * assert diffs[0].Delivery_Item == ['Y', 'N']
内容的提问来源于stack exchange,提问作者Sumith
相关产品推荐
相关产品推荐

