Repository: authorjapps/zerocode
Pull request #612 (2023-12-15 18:27:38)

ISSUE-601 # Resolve JSON.CONTENT as object or array
Fixes Issue: https://github.com/authorjapps/zerocode/issues/601

# <Feature Title>

PR Branch
**_#ADD LINK TO THE PR BRANCH_**

## Motivation and Context

## Checklist:

* [x] Unit tests added

* [x] Integration tests added

* [x] Test names are meaningful

* [x] Feature manually tested

* [x] Branch build passed

* [x] No 'package.*' in the imports

* [ ] Relevant Wiki page updated with clear instruction for the end user
  * [ ] Not applicable. This was only a refactor change, no functional or behaviour changes were introduced

* [x] Http test added to `http-testing` module(if applicable) ?
  * [ ] Not applicable. The changes did not affect HTTP automation flow

* [ ] Kafka test added to `kafka-testing` module(if applicable) ?
  * [x] Not applicable. The changes did not affect Kafka automation flow



Patch:
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessor.java b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessor.java
index 4b844fdd..0d5ac829 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessor.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessor.java
@@ -1,5 +1,6 @@
 package org.jsmart.zerocode.core.engine.preprocessor;
 
+import org.jsmart.zerocode.core.domain.Step;
 import org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher;
 import org.jsmart.zerocode.core.engine.assertion.JsonAsserter;
 
@@ -18,4 +19,7 @@ public interface ZeroCodeAssertionsProcessor {
     List<JsonAsserter> createJsonAsserters(String resolvedAssertionJson);
 
     List<FieldAssertionMatcher> assertAllAndReturnFailed(List<JsonAsserter> asserters, String executionResult);
+
+    Step resolveJsonContent(Step thisStep, ScenarioExecutionState scenarioExecutionState);
+
 }
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
index 6eacf0c7..cafb18f3 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImpl.java
@@ -1,5 +1,6 @@
 package org.jsmart.zerocode.core.engine.preprocessor;
 
+import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -20,11 +21,13 @@ import java.util.Map;
 import java.util.Properties;
 import net.minidev.json.JSONArray;
 import org.apache.commons.lang.text.StrSubstitutor;
+import org.jsmart.zerocode.core.domain.Step;
 import org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher;
 import org.jsmart.zerocode.core.engine.assertion.JsonAsserter;
 import org.jsmart.zerocode.core.engine.assertion.array.ArrayIsEmptyAsserterImpl;
 import org.jsmart.zerocode.core.engine.assertion.array.ArraySizeAsserterImpl;
 import org.jsmart.zerocode.core.engine.assertion.field.*;
+import org.jsmart.zerocode.core.utils.SmartUtils;
 
 import static java.lang.Integer.valueOf;
 import static java.lang.String.format;
@@ -32,10 +35,17 @@ import static org.apache.commons.lang.StringEscapeUtils.escapeJava;
 import static org.apache.commons.lang.StringUtils.substringBetween;
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeAssertionTokens.*;
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.$VALUE;
+import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.JSON_CONTENT;
+import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.JSON_PAYLOAD_FILE;
+import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.YAML_PAYLOAD_FILE;
 import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.deepTypeCast;
 import static org.jsmart.zerocode.core.utils.FieldTypeConversionUtils.fieldTypes;
 import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
 import static org.jsmart.zerocode.core.utils.SmartUtils.isValidAbsolutePath;
+import static org.jsmart.zerocode.core.utils.SmartUtils.readJsonAsString;
+import static org.jsmart.zerocode.core.utils.SmartUtils.readYamlAsString;
+import static org.jsmart.zerocode.core.utils.SmartUtils.checkDigNeeded;;
+import static org.jsmart.zerocode.core.utils.SmartUtils.getJsonFilePhToken;;
 import static org.jsmart.zerocode.core.utils.TokenUtils.getTestCaseTokens;
 import static org.jsmart.zerocode.core.utils.TokenUtils.populateParamMap;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -337,6 +347,39 @@ public class ZeroCodeAssertionsProcessorImpl implements ZeroCodeAssertionsProces
         return failedReports;
     }
 
+    /**
+     * Resolves JSON.CONTENT as object or array
+     *
+     * First the logic checks if dig-deep needed to avoid unwanted recursions. If not needed, the step definition is
+     * returned intact. Otherwise calls the dig deep method to perform the operation.
+     *
+     * @param thisStep
+     * @return The effective step definition
+     */
+    @Override
+    public Step resolveJsonContent(Step thisStep, ScenarioExecutionState scenarioExecutionState) {
+        try {
+            if (!checkDigNeeded(mapper, thisStep, JSON_CONTENT)) {
+                return thisStep;
+            }
+
+            JsonNode stepNode = mapper.convertValue(thisStep, JsonNode.class);
+
+            Map<String, Object> stepMap = mapper.readValue(stepNode.toString(), new TypeReference<Map<String, Object>>() {
+            });
+
+            digReplaceContent(stepMap, scenarioExecutionState);
+
+            JsonNode jsonStepNode = mapper.valueToTree(stepMap);
+
+            return mapper.treeToValue(jsonStepNode, Step.class);
+
+        } catch (Exception e) {
+            LOGGER.error("Json content reading exception - {}", e.getMessage());
+            throw new RuntimeException("Json content reading exception. Details - " + e);
+        }
+    }
+
     private void loadAnnotatedHostProperties() {
         try {
             if(isValidAbsolutePath(hostFileName)){
@@ -408,4 +451,39 @@ public class ZeroCodeAssertionsProcessorImpl implements ZeroCodeAssertionsProces
     }
 
 
+    void digReplaceContent(Map<String, Object> map, ScenarioExecutionState scenarioExecutionState) {
+        map.entrySet().forEach(entry -> {
+            Object value = entry.getValue();
+
+            if (value instanceof Map) {
+                digReplaceContent((Map<String, Object>) value, scenarioExecutionState);
+            } else {
+                LOGGER.debug("Leaf node found = {}, checking for any json content...", value);
+                if (value != null && (value.toString().contains(JSON_CONTENT))) {
+                    LOGGER.debug("Found JSON content place holder = {}. Replacing with content", value);
+                    String valueString = value.toString();
+                    String token = getJsonFilePhToken(valueString);
+
+                    if (token != null && (token.startsWith(JSON_CONTENT))) {
+                        try {
+                            String resolvedRequestJson = resolveStringJson(
+                                "${" + token.substring(JSON_CONTENT.length()) + "}",
+                                scenarioExecutionState.getResolvedScenarioState());
+                            resolvedRequestJson = resolvedRequestJson.replaceAll("\\\\", "");
+                            try {
+                                JsonNode jsonNode = mapper.readTree(resolvedRequestJson);
+                                entry.setValue(jsonNode);
+                            } catch (JsonParseException e) {
+                                //value is not a json string, but a string value
+                                entry.setValue(resolvedRequestJson);
+                            }
+                        } catch (Exception exx) {
+                            LOGGER.error("External file reference exception - {}", exx.getMessage());
+                            throw new RuntimeException(exx);
+                        }
+                    }
+                }
+            }
+        });
+    }
 }
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImpl.java b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImpl.java
index daf3ac32..b34fd1c0 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImpl.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImpl.java
@@ -13,12 +13,15 @@ import java.util.Map;
 
 import com.google.inject.name.Named;
 import org.jsmart.zerocode.core.domain.Step;
+import org.jsmart.zerocode.core.utils.SmartUtils;
 import org.slf4j.Logger;
 
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.JSON_PAYLOAD_FILE;
 import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.YAML_PAYLOAD_FILE;
 import static org.jsmart.zerocode.core.utils.SmartUtils.readJsonAsString;
 import static org.jsmart.zerocode.core.utils.SmartUtils.readYamlAsString;
+import static org.jsmart.zerocode.core.utils.SmartUtils.checkDigNeeded;
+import static org.jsmart.zerocode.core.utils.SmartUtils.getJsonFilePhToken;
 import static org.jsmart.zerocode.core.utils.TokenUtils.getTestCaseTokens;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -69,7 +72,7 @@ public class ZeroCodeExternalFileProcessorImpl implements ZeroCodeExternalFilePr
 
         try {
 
-            if (!checkDigNeeded(thisStep)) {
+            if (!checkDigNeeded(objectMapper, thisStep, JSON_PAYLOAD_FILE, YAML_PAYLOAD_FILE)) {
                 return thisStep;
             }
 
@@ -176,22 +179,4 @@ public class ZeroCodeExternalFileProcessorImpl implements ZeroCodeExternalFilePr
             }
         });
     }
-
-    private String getJsonFilePhToken(String valueString) {
-        if (valueString != null) {
-            List<String> allTokens = getTestCaseTokens(valueString);
-            if (allTokens != null && !allTokens.isEmpty()) {
-                return allTokens.get(0);
-            }
-        }
-        return null;
-    }
-
-    boolean checkDigNeeded(Step thisStep) throws JsonProcessingException {
-        String stepJson = objectMapper.writeValueAsString(thisStep);
-        List<String> allTokens = getTestCaseTokens(stepJson);
-
-        return allTokens.toString().contains(JSON_PAYLOAD_FILE) || allTokens.toString().contains(YAML_PAYLOAD_FILE);
-    }
-
 }
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/tokens/ZeroCodeValueTokens.java b/core/src/main/java/org/jsmart/zerocode/core/engine/tokens/ZeroCodeValueTokens.java
index 496fda74..3104c7a1 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/tokens/ZeroCodeValueTokens.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/tokens/ZeroCodeValueTokens.java
@@ -31,6 +31,8 @@ public class ZeroCodeValueTokens {
     public static final String SYSTEM_ENV = "SYSTEM.ENV:";
     public static final String $VALUE = ".$VALUE";
     public static final String ABS_PATH = "ABS.PATH:";
+    public static final String JSON_CONTENT = "JSON.CONTENT:";
+
 
     public static Map<String, Object> globalTokenCache = new HashMap<>();
 
diff --git a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java
index 4f7e7d05..fd9190e7 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java
@@ -181,6 +181,8 @@ public class ZeroCodeMultiStepsScenarioRunnerImpl implements ZeroCodeMultiStepsS
                                           ScenarioExecutionState scenarioExecutionState,
                                           ScenarioSpec scenario, Step thisStep) {
         thisStep = extFileProcessor.resolveExtJsonFile(thisStep);
+        thisStep = zeroCodeAssertionsProcessor.resolveJsonContent(thisStep, scenarioExecutionState);
+        
         List<Step> thisSteps = extFileProcessor.createFromStepFile(thisStep, thisStep.getId());
         if(null == thisSteps || thisSteps.isEmpty()) thisSteps.add(thisStep);
         Boolean wasExecSuccess = null;
diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
index 528659a2..819746ff 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
@@ -27,10 +27,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import org.apache.commons.lang.text.StrSubstitutor;
 import org.jsmart.zerocode.core.di.provider.ObjectMapperProvider;
 import org.jsmart.zerocode.core.domain.ScenarioSpec;
+import org.jsmart.zerocode.core.domain.Step;
 import org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,7 +39,10 @@ import static java.nio.charset.Charset.defaultCharset;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher.aMatchingMessage;
 import static org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher.aNotMatchingMessage;
+import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.JSON_PAYLOAD_FILE;
+import static org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens.YAML_PAYLOAD_FILE;
 import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadAbsoluteProperties;
+import static org.jsmart.zerocode.core.utils.TokenUtils.getTestCaseTokens;
 import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
 import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT;
 
@@ -240,4 +243,42 @@ public class SmartUtils {
             return System.getenv(envPropertyKey);
         }
     }
+
+    /**
+     *
+     * @param thisStep --> Currently executing step
+     * @param tokenString --> JSON_PAYLAOD_FILE or JSON_CONTENT
+     * @return if there is a match for the token, then the json traversal will happen
+     * @throws JsonProcessingException
+     */
+    public static boolean checkDigNeeded(ObjectMapper mapper, Step thisStep, String tokenString) throws JsonProcessingException {
+        String stepJson = mapper.writeValueAsString(thisStep);
+        List<String> allTokens = getTestCaseTokens(stepJson);
+
+        return allTokens.toString().contains(tokenString);
+    }
+    public static boolean checkDigNeeded(ObjectMapper mapper, Step thisStep, String tokenString, String alternateTokenString) throws JsonProcessingException {
+        String stepJson = mapper.writeValueAsString(thisStep);
+        List<String> allTokens = getTestCaseTokens(stepJson);
+
+        return allTokens.toString().contains(tokenString) || allTokens.toString().contains(alternateTokenString);
+    }
+
+    /**
+     * Retrieves the first token from the given value string that matches the format "${token}".
+     * Ph = Placeholder (e.g. ${JSON.FILE:unit_test_files/filebody_unit_test/common/common_content.json} )
+     *
+     * @param valueString The string from which to extract the jsonfile path
+     * @return The extracted token, or null if no token is found
+     */
+    public static String getJsonFilePhToken(String valueString) {
+        if (valueString != null) {
+            List<String> allTokens = getTestCaseTokens(valueString);
+            if (allTokens != null && !allTokens.isEmpty()) {
+                return allTokens.get(0);
+            }
+        }
+        return null;
+    }
+
 }


Test patch:
diff --git a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java
index 53187875..8d569eb7 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeAssertionsProcessorImplTest.java
@@ -1,26 +1,40 @@
 package org.jsmart.zerocode.core.engine.preprocessor;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.jayway.jsonpath.JsonPath;
+
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+
+import org.apache.commons.lang.text.StrSubstitutor;
+import org.hamcrest.core.Is;
 import org.jsmart.zerocode.core.di.main.ApplicationMainModule;
 import org.jsmart.zerocode.core.di.provider.ObjectMapperProvider;
 import org.jsmart.zerocode.core.domain.ScenarioSpec;
+import org.jsmart.zerocode.core.domain.Step;
 import org.jsmart.zerocode.core.engine.assertion.FieldAssertionMatcher;
 import org.jsmart.zerocode.core.engine.assertion.JsonAsserter;
+import org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens;
 import org.jsmart.zerocode.core.utils.SmartUtils;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
+import static com.jayway.jsonpath.JsonPath.read;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
+import static org.jsmart.zerocode.core.utils.SmartUtils.checkDigNeeded;
+import static org.jsmart.zerocode.core.utils.SmartUtils.readJsonAsString;
 import static org.jsmart.zerocode.core.utils.TokenUtils.getTestCaseTokens;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
@@ -1449,4 +1463,175 @@ public class ZeroCodeAssertionsProcessorImplTest {
         expectedException.expect(IndexOutOfBoundsException.class);
         jsonPreProcessor.resolveLeafOnlyNodeValue(scenarioState, paramMap, thisPath);
     }
+
+    @Test(expected = RuntimeException.class)
+    public void test_wrongJsonPathBy_JSONCONTENT_Exception() throws JsonProcessingException {
+        String jsonAsString = readJsonAsString("unit_test_files/json_content_unit_test/json_step_test_wrong_json_path.json");
+        Map<String, Object> map = mapper.readValue(jsonAsString, new TypeReference<Map<String, Object>>() {});
+
+        jsonPreProcessor.digReplaceContent(map, new ScenarioExecutionState());
+    }
+
+    @Test
+    public void test_JSONCONTENT_leafNode() throws IOException {
+        ScenarioExecutionState scenarioExecutionState = new ScenarioExecutionState();
+
+        final String step1 =  createStepWithRequestAndResponse("create_emp", "\"body\" : {\n    \"id\" : 39001,\n    \"ldapId\" : \"emmanorton\"\n  }\n}\n  }");
+        scenarioExecutionState.addStepState(step1);
+
+        ScenarioSpec scenarioSpec =
+            smartUtils.scenarioFileToJava(
+                "unit_test_files/json_content_unit_test/json_step_test_json_content.json", ScenarioSpec.class);
+        Step thisStep = scenarioSpec.getSteps().get(1);
+        JsonNode stepNode = mapper.convertValue(thisStep, JsonNode.class);
+        Map<String, Object> map = mapper.readValue(stepNode.toString(), new TypeReference<Map<String, Object>>() {});
+
+        jsonPreProcessor.digReplaceContent(map, scenarioExecutionState);
+
+        String jsonResult = mapper.writeValueAsString(map);
+
+        assertThat(JsonPath.read(jsonResult, "$.request.body.addressId"), is(39001));
+    }
+
+
+    @Test
+    public void test_JSONCONTENT_stringArray() throws IOException {
+        ScenarioExecutionState scenarioExecutionState = new ScenarioExecutionState();
+
+        final String step1 =  createStepWithRequestAndResponse("create_emp", "\"body\": {\"id\": 38001,\n     \"names\": [\"test1\", \"test2\"]\n}");
+        scenarioExecutionState.addStepState(step1);
+
+        ScenarioSpec scenarioSpec =
+            smartUtils.scenarioFileToJava(
+                "unit_test_files/json_content_unit_test/json_step_test_json_content_array.json", ScenarioSpec.class);
+        Step thisStep = scenarioSpec.getSteps().get(1);
+        JsonNode stepNode = mapper.convertValue(thisStep, JsonNode.class);
+        Map<String, Object> map = mapper.readValue(stepNode.toString(), new TypeReference<Map<String, Object>>() {});
+
+        jsonPreProcessor.digReplaceContent(map, scenarioExecutionState);
+
+        String jsonResult = mapper.writeValueAsString(map);
+
+        String result = "[\"test1\",\"test2\"]";
+
+        Object jsonPathValue = JsonPath.read(jsonResult,
+            "$.request.body.names");
+
+        Assert.assertEquals(result, jsonPathValue.toString());
+    }
+
+    @Test
+    public void test_JSONCONTENT_objectArray() throws IOException {
+        ScenarioExecutionState scenarioExecutionState = new ScenarioExecutionState();
+        /**
+         * {
+         *     "id": 38001,
+         *     "allAddresses": [
+         *         {
+         *             "type": "Home",
+         *             "line1": "North Lon",
+         *             "id": 43
+         *         },
+         *         {
+         *             "type": "Office",
+         *             "line1": "Central Lon"
+         *         }
+         *     ]
+         * }
+         */
+        final String step1 =  createStepWithRequestAndResponse("create_emp",
+                "\"body\": {\"id\": 38001, \"allAddresses\": [{\"type\": \"Home\", \"line1\": \"North Lon\", \"id\": 47}, {\"type\": \"Office\", \"line1\": \"Central Lon\"}]}");
+        scenarioExecutionState.addStepState(step1);
+
+        ScenarioSpec scenarioSpec =
+            smartUtils.scenarioFileToJava(
+                "unit_test_files/json_content_unit_test/json_step_test_json_content_objectarray.json", ScenarioSpec.class);
+
+        Step thisStep = scenarioSpec.getSteps().get(1);
+        JsonNode stepNode = mapper.convertValue(thisStep, JsonNode.class);
+        Map<String, Object> map = mapper.readValue(stepNode.toString(), new TypeReference<Map<String, Object>>() {});
+
+        jsonPreProcessor.digReplaceContent(map, scenarioExecutionState);
+
+        String jsonResult = mapper.writeValueAsString(map);
+
+        assertThat(JsonPath.read(jsonResult, "$.request.body.allAddresses[0].id"), is(47));
+        assertThat(JsonPath.read(jsonResult, "$.request.body.allAddresses[0].type"), is("Home"));
+        assertThat(JsonPath.read(jsonResult, "$.request.body.allAddresses[1].type"), is("Office"));
+        assertThat(JsonPath.read(jsonResult, "$.request.body.allAddresses[0].line1"), is("North Lon"));
+        assertThat(JsonPath.read(jsonResult, "$.request.body.allAddresses[1].line1"), is("Central Lon"));
+    }
+
+    @Test
+    public void test_JSONCONTENT_jsonBlock() throws IOException {
+        ScenarioExecutionState scenarioExecutionState = new ScenarioExecutionState();
+
+        final String step1 =  createStepWithRequestAndResponse("create_emp",
+                "\"body\": {\n" +
+                        "    \"id\": 38001,\n" +
+                        "    \"address\": {\n" +
+                        "        \"type\": \"Home\",\n" +
+                        "        \"line1\": \"River Side\"\n" +
+                        "    }\n" +
+                        "}");
+        scenarioExecutionState.addStepState(step1);
+
+        ScenarioSpec scenarioSpec =
+            smartUtils.scenarioFileToJava(
+                "unit_test_files/json_content_unit_test/json_step_test_json_content_block.json", ScenarioSpec.class);
+        Step thisStep = scenarioSpec.getSteps().get(1);
+        JsonNode stepNode = mapper.convertValue(thisStep, JsonNode.class);
+        Map<String, Object> map = mapper.readValue(stepNode.toString(), new TypeReference<Map<String, Object>>() {});
+
+        jsonPreProcessor.digReplaceContent(map, scenarioExecutionState);
+
+        String jsonResult = mapper.writeValueAsString(map);
+
+        assertThat(JsonPath.read(jsonResult, "$.request.body.address.type"), is("Home"));
+        assertThat(JsonPath.read(jsonResult, "$.request.body.address.line1"), is("River Side"));
+    }
+
+    @Test
+    public void test_NoJSONContentCheckDigNeeded() throws IOException {
+        String jsonAsString = readJsonAsString("unit_test_files/json_content_unit_test/json_step_no_json_content_test.json");
+        Step step = mapper.readValue(jsonAsString, Step.class);
+        assertThat(checkDigNeeded(mapper, step, ZeroCodeValueTokens.JSON_CONTENT), Is.is(false));
+
+
+        ScenarioSpec scenarioSpec =
+            smartUtils.scenarioFileToJava(
+                "unit_test_files/json_content_unit_test/json_step_test_json_content.json", ScenarioSpec.class);
+        step = scenarioSpec.getSteps().get(1);
+        assertThat(checkDigNeeded(mapper, step, ZeroCodeValueTokens.JSON_CONTENT), Is.is(true));
+    }
+
+    @Test
+    public void test_textNode() throws IOException {
+        String jsonAsString = readJsonAsString("unit_test_files/filebody_unit_test/json_step_text_request.json");
+        Step step = mapper.readValue(jsonAsString, Step.class);
+
+        jsonPreProcessor.resolveJsonContent(step, new ScenarioExecutionState());
+        String resultJsonStep = mapper.writeValueAsString(step);
+
+        assertThat(read(resultJsonStep, "$.request"), Is.is("I am a simple text"));
+    }
+
+
+    protected String createStepWithRequestAndResponse(String stepName, String body) {
+        Map<String, String> parammap = new HashMap<>();
+
+        parammap.put("STEP.NAME", stepName);
+        parammap.put("STEP.REQUEST", "{\n" +
+            "    \"customer\": {\n" +
+            "        \"firstName\": \"FIRST_NAME\"\n" +
+            "    }\n" +
+            "}");
+        parammap.put("STEP.RESPONSE", "{\n" +
+            body +
+            "}");
+
+        StrSubstitutor sub = new StrSubstitutor(parammap);
+
+        return sub.replace((new StepExecutionState()).getRequestResponseState());
+    }
 }
diff --git a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImplTest.java b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImplTest.java
index 2cd3f7da..e3083733 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImplTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeExternalFileProcessorImplTest.java
@@ -4,7 +4,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.jsmart.zerocode.core.di.provider.ObjectMapperProvider;
 import org.jsmart.zerocode.core.domain.Step;
+import org.jsmart.zerocode.core.engine.tokens.ZeroCodeValueTokens;
 import org.junit.Test;
+import static org.jsmart.zerocode.core.utils.SmartUtils.checkDigNeeded;
+
 
 import java.io.IOException;
 import java.util.Map;
@@ -79,11 +82,11 @@ public class ZeroCodeExternalFileProcessorImplTest {
     public void test_NoExtFileCheckDigNeeded() throws IOException {
         String jsonAsString = readJsonAsString("unit_test_files/filebody_unit_test/json_step_no_ext_json_test_file.json");
         Step step = objectMapper.readValue(jsonAsString, Step.class);
-        assertThat(externalFileProcessor.checkDigNeeded(step), is(false));
+        assertThat(checkDigNeeded(objectMapper, step, ZeroCodeValueTokens.JSON_PAYLOAD_FILE, ZeroCodeValueTokens.YAML_PAYLOAD_FILE), is(false));
 
         jsonAsString = readJsonAsString("unit_test_files/filebody_unit_test/json_step_text_node_ext_json_file_test.json");
         step = objectMapper.readValue(jsonAsString, Step.class);
-        assertThat(externalFileProcessor.checkDigNeeded(step), is(true));
+        assertThat(checkDigNeeded(objectMapper, step, ZeroCodeValueTokens.JSON_PAYLOAD_FILE, ZeroCodeValueTokens.YAML_PAYLOAD_FILE), is(true));
     }
 
     @Test
diff --git a/core/src/test/resources/integration_test_files/filebody/hello_world_file_request_n_response.json b/core/src/test/resources/integration_test_files/filebody/hello_world_file_request_n_response.json
index 4f521a90..42f104f7 100644
--- a/core/src/test/resources/integration_test_files/filebody/hello_world_file_request_n_response.json
+++ b/core/src/test/resources/integration_test_files/filebody/hello_world_file_request_n_response.json
@@ -22,6 +22,23 @@
                 "status": 200,
                 "body" : "${JSON.FILE:integration_test_files/filebody/bodyonly/response_body.json}"
             }
+        },
+        {
+            "name": "create_bathroom_with_name",
+            "url": "/home/bathroom/21",
+            "operation": "PUT",
+            "request": {
+                "body" : {
+                    "name": "${JSON.CONTENT:$.get_user_details.response.body.name}",
+                    "availability": true
+                }
+            },
+            "assertions": {
+                "status": 200,
+                "body": {
+                    "name": "Shower-21"
+                }
+            }
         }
     ]
 }
diff --git a/core/src/test/resources/simulators/test_purpose_end_points.json b/core/src/test/resources/simulators/test_purpose_end_points.json
index 3971527b..15dc01d6 100644
--- a/core/src/test/resources/simulators/test_purpose_end_points.json
+++ b/core/src/test/resources/simulators/test_purpose_end_points.json
@@ -43,6 +43,20 @@
                 }
             }
         },
+        {
+            "name": "PUT Bathroom - Update",
+            "operation": "PUT",
+            "url": "/home/bathroom/21",
+            "ignoreBody": true,
+            "response": {
+                "status": 200,
+                "body": {
+                    "id": 21,
+                    "name": "Shower-21",
+                    "availability": true
+                }
+            }
+        },
         {
             "name": "Get Bathroom by Id 21",
             "operation": "GET",
diff --git a/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_no_json_content_test.json b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_no_json_content_test.json
new file mode 100644
index 00000000..271d22bf
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_no_json_content_test.json
@@ -0,0 +1,21 @@
+{
+  "name": "get_user_details",
+  "url": "/api/v1/employees/${$.create_emp.response.body.id}",
+  "method": "GET",
+  "request": {
+    "body" : {
+      "fullName":"Bob Luis",
+      "empLogin":"bolu_lon",
+      "type":"CONTRACT"
+    }
+  },
+  "verify": {
+    "status": 200,
+    "body": {
+      "id": 39001,
+      "ldapId": "emmanorton",
+      "name": "Emma",
+      "surName": "Norton"
+    }
+  }
+}
diff --git a/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content.json b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content.json
new file mode 100644
index 00000000..1a8af01e
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content.json
@@ -0,0 +1,41 @@
+{
+  "scenarioName": "POST API - File json as request content - Reuse body",
+  "steps": [
+    {
+      "name": "create_emp",
+      "url": "/api/v1/employees",
+      "method": "POST",
+      "request": {
+        "body": {
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      },
+      "verify": {
+        "status": 201
+      }
+    },
+    {
+      "name": "get_user_details",
+      "url": "/api/v1/employees/${$.create_emp.response.body.id}",
+      "method": "GET",
+      "request": {
+        "body": {
+          "fullName": "Bob Luis",
+          "empLogin": "bolu_lon",
+          "type": "CONTRACT",
+          "addressId": "${JSON.CONTENT:$.create_emp.response.body.id}"
+        }
+      },
+      "verify": {
+        "status": 200,
+        "body": {
+          "id": 39001,
+          "ldapId": "emmanorton",
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      }
+    }
+  ]
+}
diff --git a/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_array.json b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_array.json
new file mode 100644
index 00000000..8c0f619a
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_array.json
@@ -0,0 +1,41 @@
+{
+  "scenarioName": "POST API - File json as request content - Reuse body",
+  "steps": [
+    {
+      "name": "create_emp",
+      "url": "/api/v1/employees",
+      "method": "POST",
+      "request": {
+        "body": {
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      },
+      "verify": {
+        "status": 201
+      }
+    },
+    {
+      "name": "get_user_details",
+      "url": "/api/v1/employees/${$.create_emp.response.body.id}",
+      "method": "GET",
+      "request": {
+        "body": {
+          "fullName": "Bob Luis",
+          "empLogin": "bolu_lon",
+          "type": "CONTRACT",
+          "names": "${JSON.CONTENT:$.create_emp.response.body.names}"
+        }
+      },
+      "verify": {
+        "status": 200,
+        "body": {
+          "id": 39001,
+          "ldapId": "emmanorton",
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      }
+    }
+  ]
+}
diff --git a/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_block.json b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_block.json
new file mode 100644
index 00000000..b287b99d
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_block.json
@@ -0,0 +1,41 @@
+{
+  "scenarioName": "POST API - File json as request content - Reuse body",
+  "steps": [
+    {
+      "name": "create_emp",
+      "url": "/api/v1/employees",
+      "method": "POST",
+      "request": {
+        "body": {
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      },
+      "verify": {
+        "status": 201
+      }
+    },
+    {
+      "name": "get_user_details",
+      "url": "/api/v1/employees/${$.create_emp.response.body.id}",
+      "method": "GET",
+      "request": {
+        "body": {
+          "fullName": "Bob Luis",
+          "empLogin": "bolu_lon",
+          "type": "CONTRACT",
+          "address": "${JSON.CONTENT:$.create_emp.response.body.address}"
+        }
+      },
+      "verify": {
+        "status": 200,
+        "body": {
+          "id": 39001,
+          "ldapId": "emmanorton",
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      }
+    }
+  ]
+}
diff --git a/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_objectarray.json b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_objectarray.json
new file mode 100644
index 00000000..daf4b95f
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_json_content_objectarray.json
@@ -0,0 +1,41 @@
+{
+  "scenarioName": "POST API - File json as request content - Reuse body",
+  "steps": [
+    {
+      "name": "create_emp",
+      "url": "/api/v1/employees",
+      "method": "POST",
+      "request": {
+        "body": {
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      },
+      "verify": {
+        "status": 201
+      }
+    },
+    {
+      "name": "get_user_details",
+      "url": "/api/v1/employees/${$.create_emp.response.body.id}",
+      "method": "GET",
+      "request": {
+        "body": {
+          "fullName": "Bob Luis",
+          "empLogin": "bolu_lon",
+          "type": "CONTRACT",
+          "allAddresses": "${JSON.CONTENT:$.create_emp.response.body.allAddresses}"
+        }
+      },
+      "verify": {
+        "status": 200,
+        "body": {
+          "id": 39001,
+          "ldapId": "emmanorton",
+          "name": "Emma",
+          "surName": "Norton"
+        }
+      }
+    }
+  ]
+}
diff --git a/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_wrong_json_path.json b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_wrong_json_path.json
new file mode 100644
index 00000000..5fe61831
--- /dev/null
+++ b/core/src/test/resources/unit_test_files/json_content_unit_test/json_step_test_wrong_json_path.json
@@ -0,0 +1,22 @@
+{
+  "name": "get_user_details",
+  "url": "/api/v1/employees/${$.create_emp.response.body.id}",
+  "method": "GET",
+  "request": {
+    "body" : {
+      "fullName":"Bob Luis",
+      "empLogin":"bolu_lon",
+      "type":"CONTRACT",
+      "address":"${JSON.CONTENT:$.create_emp.response.body.id}"
+    }
+  },
+  "verify": {
+    "status": 200,
+    "body": {
+      "id": 39001,
+      "ldapId": "emmanorton",
+      "name": "Emma",
+      "surName": "Norton"
+    }
+  }
+}
diff --git a/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldjsoncontent/HelloJsonContentAsBodyTest.java b/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldjsoncontent/HelloJsonContentAsBodyTest.java
new file mode 100644
index 00000000..148a5a91
--- /dev/null
+++ b/http-testing/src/test/java/org/jsmart/zerocode/testhelp/tests/helloworldjsoncontent/HelloJsonContentAsBodyTest.java
@@ -0,0 +1,18 @@
+package org.jsmart.zerocode.testhelp.tests.helloworldjsoncontent;
+
+import org.jsmart.zerocode.core.domain.JsonTestCase;
+import org.jsmart.zerocode.core.domain.TargetEnv;
+import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@TargetEnv("hello_world_host.properties")
+@RunWith(ZeroCodeUnitRunner.class)
+public class HelloJsonContentAsBodyTest {
+
+	@Test
+	@JsonTestCase("helloworld_json_content/hello_world_json_content_as_request_body.json")
+	public void testHelloWorld_jsonContentAsBody() throws Exception {
+	}
+
+}
diff --git a/http-testing/src/test/resources/helloworld_json_content/hello_world_json_content_as_request_body.json b/http-testing/src/test/resources/helloworld_json_content/hello_world_json_content_as_request_body.json
new file mode 100644
index 00000000..bf3cf2a7
--- /dev/null
+++ b/http-testing/src/test/resources/helloworld_json_content/hello_world_json_content_as_request_body.json
@@ -0,0 +1,39 @@
+{
+  "scenarioName": "POST API - JSON content as request body",
+  "steps": [
+    {
+      "name": "get_screening",
+      "url": "/api/v1/employees/screening/SCRUNIQUEID5003",
+      "method": "GET",
+      "request": {},
+      "assertions": {
+        "status": 200,
+        "body": {
+          "id": "SCRUNIQUEID5003",
+          "empId": "EMP39001",
+          "originAddress": {
+            "addressId":"lon-hsbc-5432",
+            "countryOfOrigin":"UK"
+          }
+        }
+      }
+    },
+    {
+      "name": "create_screening",
+      "url": "/api/v1/employees/screening",
+      "method": "POST",
+      "request": {
+        "body": {
+          "empId": "${JSON.CONTENT:$.get_screening.response.body.empId}",
+          "originAddress":"${JSON.CONTENT:$.get_screening.response.body.originAddress}"
+        }
+      },
+      "assertions": {
+        "status": 201,
+        "body": {
+          "id": "SCRUNIQUEID5003"
+        }
+      }
+    }
+  ]
+}