This is an automated email from the ASF dual-hosted git repository.

npeltier pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-pipes.git


The following commit(s) were added to refs/heads/master by this push:
     new 5660af9  SLING-10251 make JsonPipe able to loop on objects
5660af9 is described below

commit 5660af99a75f36915bbd5b226c459952cb972f30
Author: Nicolas Peltier <[email protected]>
AuthorDate: Tue Mar 23 18:55:24 2021 +0100

    SLING-10251 make JsonPipe able to loop on objects
---
 .../java/org/apache/sling/pipes/OutputWriter.java  |  2 -
 .../sling/pipes/internal/inputstream/JsonPipe.java | 56 +++++++++++++++-------
 .../pipes/internal/inputstream/JsonPipeTest.java   | 24 +++++++++-
 src/test/resources/json.json                       |  1 +
 4 files changed, 62 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/apache/sling/pipes/OutputWriter.java 
b/src/main/java/org/apache/sling/pipes/OutputWriter.java
index 9c020d0..9e22a70 100644
--- a/src/main/java/org/apache/sling/pipes/OutputWriter.java
+++ b/src/main/java/org/apache/sling/pipes/OutputWriter.java
@@ -25,14 +25,12 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.io.Writer;
-import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
diff --git 
a/src/main/java/org/apache/sling/pipes/internal/inputstream/JsonPipe.java 
b/src/main/java/org/apache/sling/pipes/internal/inputstream/JsonPipe.java
index a1a2803..8ff0a91 100644
--- a/src/main/java/org/apache/sling/pipes/internal/inputstream/JsonPipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/inputstream/JsonPipe.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sling.pipes.internal.inputstream;
 
+import org.apache.commons.collections.keyvalue.DefaultKeyValue;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.resource.Resource;
@@ -34,11 +35,13 @@ import javax.json.JsonValue.ValueType;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * Pipe outputting binding related to a json stream: either an object
@@ -52,6 +55,11 @@ public class JsonPipe extends AbstractInputStreamPipe {
      */
     protected static final String PN_VALUEPATH = "valuePath";
 
+    /**
+     * property specifying wether we should bind the computed json as a whole, 
or loop over it (not set or raw=false)
+     */
+    protected static final String PN_RAW = "raw";
+
     protected static final String JSONPATH_ROOT = "$";
 
     protected static final String ARRAY_START = "[";
@@ -59,12 +67,24 @@ public class JsonPipe extends AbstractInputStreamPipe {
     protected static final String OBJ_START = ".";
 
     protected static final Pattern JSONPATH_FIRSTTOKEN = Pattern.compile("^\\" 
+ JSONPATH_ROOT + "([\\" + OBJ_START + "\\" + ARRAY_START + "])([^\\" + 
OBJ_START + "\\]\\" + ARRAY_START + "]+)\\]?");
-    ArrayBasedIterator internalIterator;
+
+    JsonBindingIterator internalIterator;
 
     public JsonPipe(Plumber plumber, Resource resource, PipeBindings 
upperBindings) {
         super(plumber, resource, upperBindings);
     }
 
+    boolean isRaw() {
+        if (properties.containsKey(PN_RAW)) {
+            Object raw = bindings.instantiateObject(properties.get(PN_RAW, 
String.class));
+            if (raw != null && raw instanceof Boolean) {
+                return (Boolean) raw;
+            }
+            return Boolean.parseBoolean((String)raw);
+        }
+        return false;
+    }
+
     /**
      * in case there is no successful retrieval of some JSON data, we cut the 
pipe here
      * @return input resource of the pipe, can be reouputed N times in case 
output json binding is an array of
@@ -86,12 +106,12 @@ public class JsonPipe extends AbstractInputStreamPipe {
                     if (StringUtils.isNotBlank(valuePath)) {
                         json = getValue(json, valuePath);
                     }
-                    if (json.getValueType() != ValueType.ARRAY) {
+                    if (isRaw() || !(json.getValueType() == ValueType.ARRAY || 
json.getValueType() == ValueType.OBJECT)) {
                         binding = JsonUtil.unbox(json);
                         output = inputSingletonIterator;
                     } else {
                         binding = json;
-                        output = internalIterator = new 
ArrayBasedIterator((JsonArray)binding, getInput());
+                        output = internalIterator = new 
JsonBindingIterator(json, getInput());
                     }
                 }
             }
@@ -101,32 +121,32 @@ public class JsonPipe extends AbstractInputStreamPipe {
         return output;
     }
 
-    private class ArrayBasedIterator implements Iterator<Resource> {
-        int index = 0;
-        JsonArray array;
-
+    class JsonBindingIterator implements Iterator<Resource> {
+        Collection<Object> jsonValues;
+        Iterator<Object> internal;
         final Resource inputResource;
 
-        ArrayBasedIterator(JsonArray array, Resource inputResource) {
-            this.array = array;
+        JsonBindingIterator(JsonStructure json, Resource inputResource) {
+            jsonValues = json.getValueType() == ValueType.ARRAY ?
+                    ((JsonArray) 
json).stream().map(JsonUtil::unbox).collect(Collectors.toList()):
+                    ((JsonObject) json).entrySet().stream()
+                            .map(e -> new DefaultKeyValue(e.getKey(),
+                                    
JsonUtil.unbox(e.getValue()))).collect(Collectors.toList());
+            internal = jsonValues.iterator();
             this.inputResource = inputResource;
         }
+
         @Override
         public boolean hasNext() {
-            return index < array.size();
+            return internal.hasNext();
         }
 
         @Override
         public Resource next() {
-            try {
-                if (index >= array.size()) {
-                    throw new NoSuchElementException();
-                }
-                binding = JsonUtil.unbox(array.get(index));
-            } catch (Exception e) {
-                logger.error("Unable to retrieve {}nth item of jsonarray", 
index, e);
+            if (! internal.hasNext()) {
+                throw new NoSuchElementException();
             }
-            index++;
+            binding = internal.next();
             return inputResource;
         }
     }
diff --git 
a/src/test/java/org/apache/sling/pipes/internal/inputstream/JsonPipeTest.java 
b/src/test/java/org/apache/sling/pipes/internal/inputstream/JsonPipeTest.java
index 6597f40..6539fad 100644
--- 
a/src/test/java/org/apache/sling/pipes/internal/inputstream/JsonPipeTest.java
+++ 
b/src/test/java/org/apache/sling/pipes/internal/inputstream/JsonPipeTest.java
@@ -17,6 +17,7 @@
 package org.apache.sling.pipes.internal.inputstream;
 
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import org.apache.commons.collections4.IteratorUtils;
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ValueMap;
@@ -29,10 +30,12 @@ import org.junit.Test;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.Iterator;
+import java.util.List;
 
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
 import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -107,13 +110,32 @@ public class JsonPipeTest extends AbstractPipeTest {
         http.givenThat(get(urlEqualTo("/get/foo.json"))
                 
.willReturn(aResponse().withStatus(200).withBody("{\"args\":{\"foo1\":\"bar\"}}")));
         ExecutionResult results = execute("echo /content " +
-                "| json "  + baseUrl + "/get/foo.json @ name data " +
+                "| json "  + baseUrl + "/get/foo.json @ name data @ with 
raw=true " +
                 "| mkdir ${data.args.foo1}");
         assertEquals(1, results.size());
         assertEquals("/content/bar", 
results.getCurrentPathSet().iterator().next());
     }
 
     @Test
+    public void testLoopOverObject() throws InvocationTargetException, 
IllegalAccessException {
+        ExecutionResult results = execute("echo /content " +
+                "| json {'k1':'v1','k2':'v2'} @ name j" +
+                "| mkdir ${j.key}/${j.value}");
+        assertEquals(2, results.size());
+        List<String> array = 
IteratorUtils.toList(results.getCurrentPathSet().iterator());
+        assertArrayEquals(new String[] {"/content/k2/v2", "/content/k1/v1"}, 
array.toArray());
+    }
+
+    @Test
+    public void testRaw() throws InvocationTargetException, 
IllegalAccessException {
+        ExecutionResult results = execute("echo /content " +
+                "| json {'k1':'v1','k2':'v2'} @ name j @ with raw=true" +
+                "| write test=${j.k2}");
+        assertEquals(1, results.size());
+        assertEquals("v2", 
context.resourceResolver().getResource("/content").getValueMap().get("test", 
String.class));
+    }
+
+    @Test
     @Ignore
     public void testAuthentifiedRemoteJson() throws InvocationTargetException, 
IllegalAccessException {
         
http.givenThat(get(urlEqualTo("/get/profile.json")).withBasicAuth("jdoe","jdoe")
diff --git a/src/test/resources/json.json b/src/test/resources/json.json
index 60465df..9d06afa 100644
--- a/src/test/resources/json.json
+++ b/src/test/resources/json.json
@@ -25,6 +25,7 @@
         "weather": {
           "jcr:primaryType": "nt:unstructured",
           "sling:resourceType": "slingPipes/json",
+          "raw": true,
           "expr": "{'query':{'ro':{'city':'Bucharest','temp':'12°'}, 
'fr':{'city':'Paris','temp':'13°'}}}"
         },
         "write": {

Reply via email to