From ff8f71abf456be7df394b533043c0cb04ba79b82 Mon Sep 17 00:00:00 2001 From: Andreas Isler Date: Tue, 25 Nov 2025 11:58:10 +0100 Subject: [PATCH] Moved isSubset to mapper test --- .../example/parser/FlowableExcelMapper.java | 53 +---------------- .../parser/FlowableExcelMapperTest.java | 55 +++++++++++++++++- .../flowableExcelMapperTestData.xlsx | Bin 9733 -> 13433 bytes 3 files changed, 53 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/example/parser/FlowableExcelMapper.java b/src/main/java/com/example/parser/FlowableExcelMapper.java index 14246c1..b28515e 100644 --- a/src/main/java/com/example/parser/FlowableExcelMapper.java +++ b/src/main/java/com/example/parser/FlowableExcelMapper.java @@ -75,9 +75,7 @@ public class FlowableExcelMapper { protected JsonNode rowToJsonNode(ArrayList types, ArrayList paths, ArrayList row, JsonNode jsonNode) { for (int cellNum = 0; cellNum < row.size(); cellNum++) { String cellValue = row.get(cellNum); - if (cellValue == null || cellValue.isEmpty()) { - jsonNode = addNode(jsonNode, paths.get(cellNum), null); - } else { + if (cellValue != null && !cellValue.isEmpty()) { String type = types.get(cellNum); Object content; switch (type) { @@ -175,53 +173,4 @@ public class FlowableExcelMapper { } return -1; } - - public boolean isSubset(JsonNode asIs, JsonNode toBe) { - // If toBe is null, it is always a subset - if (toBe == null) { - return true; - } - // If asIs is null, but toBe is not, toBe is not a subset - if (asIs == null) { - return false; - } - // If toBo is a value node, compare values - if (toBe.isValueNode()) { - return asIs.isValueNode() && asIs.asText().equals(toBe.asText()); - } - // If toBe is an array node, check is all elements of toBe exist in asIs - if(toBe.isArray()){ - if (!asIs.isArray() || toBe.size() > asIs.size()) { - return false; - } - for (JsonNode toBeElement : toBe) { - boolean found = false; - for (JsonNode asIsElement : asIs) { - if (isSubset(asIsElement, toBeElement)) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - return true; - } - // If toBe is an object node, check if all fields in toBe exist in asIs - if (toBe.isObject()){ - if (!asIs.isObject()){ - return false; - } - for (Iterator iterator = toBe.fieldNames(); iterator.hasNext(); ) { - String fieldName = iterator.next(); - if (!asIs.has(fieldName) || !isSubset(asIs.get(fieldName), toBe.get(fieldName))){ - return false; - } - } - return true; - } - // If none of the above matches, return false - return false; - } } diff --git a/src/test/java/com/example/parser/FlowableExcelMapperTest.java b/src/test/java/com/example/parser/FlowableExcelMapperTest.java index 415cc8d..0351650 100644 --- a/src/test/java/com/example/parser/FlowableExcelMapperTest.java +++ b/src/test/java/com/example/parser/FlowableExcelMapperTest.java @@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.ArrayList; +import java.util.Iterator; import java.util.stream.Stream; @@ -22,8 +23,55 @@ class FlowableExcelMapperTest { @Autowired private FlowableExcelMapper flowableExcelMapper; - private static final Logger logger = LoggerFactory.getLogger(FlowableExcelMapperTest.class); + public boolean isSubset(JsonNode root, JsonNode test) { + // If test is null, it is always a subset of root + if (test == null) { + return true; + } + // If root is null, but test is not, test is not a subset of root + if (root == null) { + return false; + } + // If test is a value node, compare values + if (test.isValueNode()) { + return root.isValueNode() && root.asText().equals(test.asText()); + } + // If test is an array node, check if all elements of test exist in root + if(test.isArray()){ + if (!root.isArray() || test.size() > root.size()) { + return false; + } + for (JsonNode rootElement : test) { + boolean found = false; + for (JsonNode testElement : root) { + if (isSubset(testElement, rootElement)) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; + } + // If test is an object node, check if all fields in test exist in root + if (test.isObject()){ + if (!root.isObject()){ + return false; + } + for (Iterator iterator = test.fieldNames(); iterator.hasNext(); ) { + String fieldName = iterator.next(); + if (!root.has(fieldName) || !isSubset(root.get(fieldName), test.get(fieldName))){ + return false; + } + } + return true; + } + // If none of the above matches, return false + return false; + } protected Stream getArgumentsFromExcel(String path) { ArrayList argumentList = new ArrayList<>(); @@ -44,7 +92,8 @@ class FlowableExcelMapperTest { public void excelMapperTest(JsonNode row) { JsonNode asIs = row.get("root"); JsonNode toBe = row.get("test"); - logger.info("{}asIs= {}{}toBe= {}{}", System.lineSeparator(), asIs.toPrettyString(), System.lineSeparator(), toBe.toPrettyString(), System.lineSeparator()); - Assertions.assertThat(flowableExcelMapper.isSubset(asIs, toBe)).isTrue(); + //logger.info("{}root={}{}test={}", System.lineSeparator(), asIs, System.lineSeparator(), toBe); + Assertions.assertThat(isSubset(asIs, toBe)) + .withFailMessage(System.lineSeparator() + "root=" + asIs + System.lineSeparator() + "test=" + toBe).isTrue(); } } diff --git a/src/test/resources/flowableExcelMapperTestData.xlsx b/src/test/resources/flowableExcelMapperTestData.xlsx index 0cd15195ea8b800f40f10bd3cca645172bde4dbf..b36a75f6fdf5d222541953b56e333ad2211eb629 100644 GIT binary patch delta 11070 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 qPjBy`m&yo_=b`2PZ&I*#1B1^i0+O