Repository: wiremock/wiremock
Issue: Result (emptyJson) returned immediately without being stored in a variable, if any

https://github.com/wiremock/wiremock/blob/25b82f9f97f3cc09136097b249cfd37aef924c36/src/main/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelper.java#L60

Result (emptyJson) returned immediately without being stored in a variable, if any

Changed files:
src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/ResponseTemplateTransformerTest.java
src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelperTest.java
src/main/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelper.java
src/main/java/com/github/tomakehurst/wiremock/stubbing/StubMappingJsonRecorder.java

diff --git a/src/main/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelper.java b/src/main/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelper.java
index 6260fa064..e7f42f687 100644
--- a/src/main/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelper.java
+++ b/src/main/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 Thomas Akehurst
+ * Copyright (C) 2021-2023 Thomas Akehurst
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -57,13 +57,13 @@ public class ParseJsonHelper extends HandlebarsHelper<Object> {
       // Edge case if JSON object is empty {}
       String jsonAsStringWithoutSpace = jsonAsString.replaceAll("\\\\s", "");
       if (jsonAsStringWithoutSpace.equals("{}") || jsonAsStringWithoutSpace.equals("")) {
-        return result;
-      }
-
-      if (jsonAsString.startsWith("[") && jsonAsString.endsWith("]")) {
-        result = Json.read(jsonAsString, new TypeReference<List<Object>>() {});
+        result = new HashMap<String, Object>();
       } else {
-        result = Json.read(jsonAsString, new TypeReference<Map<String, Object>>() {});
+        if (jsonAsString.startsWith("[") && jsonAsString.endsWith("]")) {
+          result = Json.read(jsonAsString, new TypeReference<List<Object>>() {});
+        } else {
+          result = Json.read(jsonAsString, new TypeReference<Map<String, Object>>() {});
+        }
       }
     }
 
diff --git a/src/main/java/com/github/tomakehurst/wiremock/stubbing/StubMappingJsonRecorder.java b/src/main/java/com/github/tomakehurst/wiremock/stubbing/StubMappingJsonRecorder.java
index ad973c9da..2f3195651 100644
--- a/src/main/java/com/github/tomakehurst/wiremock/stubbing/StubMappingJsonRecorder.java
+++ b/src/main/java/com/github/tomakehurst/wiremock/stubbing/StubMappingJsonRecorder.java
@@ -33,9 +33,7 @@ import java.util.List;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
-/**
- * @deprecated this is the legacy recorder and will be removed before 3.x is out of beta
- */
+/** @deprecated this is the legacy recorder and will be removed before 3.x is out of beta */
 @Deprecated
 public class StubMappingJsonRecorder implements RequestListener {
 
diff --git a/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/ResponseTemplateTransformerTest.java b/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/ResponseTemplateTransformerTest.java
index 2ac9bd44d..fadedf8c6 100644
--- a/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/ResponseTemplateTransformerTest.java
+++ b/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/ResponseTemplateTransformerTest.java
@@ -914,6 +914,26 @@ public class ResponseTemplateTransformerTest {
     assertThat(transform("{{parseJson}}"), is("[ERROR: Missing required JSON string parameter]"));
   }
 
+  @Test
+  public void parsesEmptyJsonLiteralToAnEmptyMap() {
+    String result = transform("{{#parseJson 'parsedObj'}}\\n" + "{\\n" + "}\\n" + "{{/parseJson}}\\n");
+
+    assertThat(result, equalToCompressingWhiteSpace(""));
+  }
+
+  @Test
+  public void parsesEmptyJsonVariableToAnEmptyMap() {
+    String result =
+        transform(
+            "{{#assign 'json'}}\\n"
+                + "{\\n"
+                + "}\\n"
+                + "{{/assign}}\\n"
+                + "{{parseJson json 'parsedObj'}}\\n");
+
+    assertThat(result, equalToCompressingWhiteSpace(""));
+  }
+
   @Test
   public void conditionalBranchingOnStringMatchesRegexInline() {
     assertThat(transform("{{#if (matches '123' '[0-9]+')}}YES{{/if}}"), is("YES"));
diff --git a/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelperTest.java b/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelperTest.java
index b5caba622..0bf38da67 100644
--- a/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelperTest.java
+++ b/src/test/java/com/github/tomakehurst/wiremock/extension/responsetemplating/helpers/ParseJsonHelperTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 Thomas Akehurst
+ * Copyright (C) 2021-2023 Thomas Akehurst
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,8 +16,16 @@
 package com.github.tomakehurst.wiremock.extension.responsetemplating.helpers;
 
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-
+import static org.hamcrest.Matchers.aMapWithSize;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.hasKey;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
+import static org.hamcrest.Matchers.nullValue;
+
+import com.github.jknack.handlebars.Handlebars;
 import com.github.jknack.handlebars.Options;
 import com.github.jknack.handlebars.TagType;
 import com.github.jknack.handlebars.Template;
@@ -116,10 +124,54 @@ public class ParseJsonHelperTest extends HandlebarsHelperTestBase {
 
   @Test
   public void parsesEmptyJsonIfSection() throws Exception {
+    String inputJson = "";
+    String variableName = "parsedObject";
+    Template template = new Handlebars().compileInline(inputJson);
+    Options options =
+        new Options.Builder(null, null, TagType.SECTION, createContext(), template)
+            .setParams(new Object[] {})
+            .build();
+    Object output = render(variableName, options);
+
+    // Check that it returns null
+    assertThat(output, is(nullValue()));
+
+    /* Check that it stores parsed json (an empty map in this case because json is empty)
+     * in given variable name */
+    Object storedData = options.data(variableName);
+    assertThat(storedData, isA(Map.class));
+    Map<String, Object> castedData = (Map<String, Object>) storedData;
+    assertThat(castedData, is(aMapWithSize(0)));
+  }
+
+  @Test
+  public void parsesEmptyJsonWithBracesIfSection() throws Exception {
     String inputJson = "{}";
-    Object output = render(inputJson, new Object[] {}, TagType.SECTION);
+    String variableName = "parsedObject";
+    Template template = new Handlebars().compileInline(inputJson);
+    Options options =
+        new Options.Builder(null, null, TagType.SECTION, createContext(), template)
+            .setParams(new Object[] {})
+            .build();
+    Object output = render(variableName, options);
+
+    // Check that it returns null
+    assertThat(output, is(nullValue()));
+
+    /* Check that it stores parsed json (an empty map in this case because json is empty)
+     * in given variable name */
+    Object storedData = options.data(variableName);
+    assertThat(storedData, isA(Map.class));
+    Map<String, Object> castedData = (Map<String, Object>) storedData;
+    assertThat(castedData, is(aMapWithSize(0)));
+  }
 
-    // Check that it returns empty object
+  @Test
+  public void parsesEmptyJsonIfSectionIfVariableNameNull() throws Exception {
+    String variableName = null;
+    Object output = render(variableName, new Object[] {}, TagType.SECTION);
+
+    // Check that it returns empty object because variable name is null
     assertThat(output, instanceOf(Map.class));
     Map<String, Object> result = (Map<String, Object>) output;
     assertThat(result, aMapWithSize(0));
@@ -127,20 +179,68 @@ public class ParseJsonHelperTest extends HandlebarsHelperTestBase {
 
   @Test
   public void parsesEmptyJsonIfNotSection() throws Exception {
+    String inputJson = "";
+    String variableName = "parsedObject";
+    Object[] params = {variableName};
+    Options options =
+        new Options.Builder(null, null, TagType.VAR, createContext(), Template.EMPTY)
+            .setParams(params)
+            .build();
+    Object output = render(inputJson, options);
+
+    // Check that it returns empty object
+    assertThat(output, is(nullValue()));
+
+    /* Check that it stores parsed json (an empty map in this case because json is empty)
+     * in given variable name */
+    Object storedData = options.data(variableName);
+    assertThat(storedData, isA(Map.class));
+    Map<String, Object> castedData = (Map<String, Object>) storedData;
+    assertThat(castedData, is(aMapWithSize(0)));
+  }
+
+  @Test
+  public void parsesEmptyJsonWithBracesIfNotSection() throws Exception {
     String inputJson = "{}";
-    Object output = render(inputJson, new Object[] {}, TagType.VAR);
+    String variableName = "parsedObject";
+    Object[] params = {variableName};
+    Options options =
+        new Options.Builder(null, null, TagType.VAR, createContext(), Template.EMPTY)
+            .setParams(params)
+            .build();
+    Object output = render(inputJson, options);
 
     // Check that it returns empty object
+    assertThat(output, is(nullValue()));
+
+    /* Check that it stores parsed json (an empty map in this case because json is empty)
+     * in given variable name */
+    Object storedData = options.data(variableName);
+    assertThat(storedData, isA(Map.class));
+    Map<String, Object> castedData = (Map<String, Object>) storedData;
+    assertThat(castedData, is(aMapWithSize(0)));
+  }
+
+  @Test
+  public void parsesEmptyJsonIfNotSectionIfVariableNameAbsent() throws Exception {
+    String inputJson = "{}";
+    Object output = render(inputJson, new Object[] {}, TagType.VAR);
+
+    // Check that it returns empty object because variable name is null
     assertThat(output, instanceOf(Map.class));
     Map<String, Object> result = (Map<String, Object>) output;
     assertThat(result, aMapWithSize(0));
   }
 
   private Object render(Object context, Object[] params, TagType tagType) throws IOException {
-    return helper.apply(
+    return render(
         context,
         new Options.Builder(null, null, tagType, createContext(), Template.EMPTY)
             .setParams(params)
             .build());
   }
+
+  private Object render(Object context, Options options) throws IOException {
+    return helper.apply(context, options);
+  }
 }