diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java index 2fcd3a33..bfb489b9 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/function/PathFunctionFactory.java @@ -9,6 +9,7 @@ import com.jayway.jsonpath.internal.function.numeric.Min; import com.jayway.jsonpath.internal.function.numeric.StandardDeviation; import com.jayway.jsonpath.internal.function.numeric.Sum; import com.jayway.jsonpath.internal.function.text.Concatenate; +import com.jayway.jsonpath.internal.function.text.Join; import com.jayway.jsonpath.internal.function.text.Length; import java.util.Collections; @@ -40,6 +41,7 @@ public class PathFunctionFactory { // Text Functions map.put("concat", Concatenate.class); + map.put("join", Join.class); // JSON Entity Functions map.put(Length.TOKEN_NAME, Length.class); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Join.java b/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Join.java new file mode 100644 index 00000000..fbb7c499 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Join.java @@ -0,0 +1,47 @@ +package com.jayway.jsonpath.internal.function.text; + +import com.jayway.jsonpath.internal.EvaluationContext; +import com.jayway.jsonpath.internal.PathRef; +import com.jayway.jsonpath.internal.function.Parameter; +import com.jayway.jsonpath.internal.function.PathFunction; + +import java.util.List; + +public class Join implements PathFunction { + @Override + public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) { + StringBuilder result = new StringBuilder(); + + List strings = null; + String separator = " "; + if (null != parameters){ + strings = Parameter.toList(String.class, ctx, parameters); + if (strings.size() == 0) + separator = " "; + else + separator = strings.remove(strings.size() - 1); + } + + if(ctx.configuration().jsonProvider().isArray(model)){ + Iterable objects = ctx.configuration().jsonProvider().toIterable(model); + for (Object obj : objects) { + if (obj instanceof String) { + result.append(obj.toString()); + result.append(separator); + } + } + } + + if (null != strings){ + for (String string : strings){ + result.append(string); + result.append(separator); + } + } + + if (result.length() > 0) + return result.delete(result.length() - separator.length(), result.length()).toString(); + else + return result; + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index a118f9ca..97664e46 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -42,6 +42,8 @@ public class PathCompiler { private static final char SINGLE_QUOTE = '\''; private static final char DOUBLE_QUOTE = '"'; + private static final char ESACAPE = '\\'; + private final LinkedList filterStack; private final CharacterIndex path; @@ -364,6 +366,15 @@ public class PathCompiler { } } break; + case ESACAPE: + char next = path.currentChar(); + if (groupQuote == 1 && (next == ',' || next == '\"')){ + parameter.append(next); + path.incrementPosition(1); + priorChar = 0; + continue; + } + break; } if (type != null && !(c == COMMA && 0 == groupBrace && 0 == groupBracket && 1 == groupParen)) { diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue623.java b/json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue623.java new file mode 100644 index 00000000..5151a640 --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue623.java @@ -0,0 +1,49 @@ +package com.jayway.jsonpath.internal.function; + +import com.jayway.jsonpath.Configuration; +import com.jayway.jsonpath.Configurations; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@RunWith(Parameterized.class) +public class Issue623 extends BaseFunctionTest{ + private static final Logger logger = LoggerFactory.getLogger(NumericPathFunctionTest.class); + private Configuration conf; + + @Parameterized.Parameters + public static Iterable configurations() { + return Configurations.configurations(); + } + + public Issue623(Configuration conf) { + logger.debug("Testing with configuration {}", conf.getClass().getName()); + this.conf = conf; + } + + @Test + public void joinFunctionSimpleTest() { + verifyTextFunction(conf, "$.text.join()", "a b c d e f"); + verifyTextFunction(conf, "$.text.join(\"\\, \")", "a, b, c, d, e, f"); + verifyTextFunction(conf, "$.text.join(\"1\", \"2\", \"3\", \"-\")", "a-b-c-d-e-f-1-2-3"); + } + + @Test + public void testJoinNestedTest() { + String json = "{\"document\" : { " + + "\"text\" : [ " + + "{ \"value\": \"a\", \"int\": \"1\" }, " + + "{ \"value\": \"b\", \"int\": \"2\" }, " + + "{ \"value\": \"c\", \"int\": \"3\" }, " + + "{ \"value\": \"d\", \"int\": \"4\" }, " + + "{ \"value\": \"e\", \"int\": \"5\" }, " + + "{ \"value\": \"f\", \"int\": \"6\" } ]}}"; + + verifyFunction(conf, "join(" + + "$.join($.document.text[*]['value'], \"\t\"), " + + "$.join($.document.text[*]['int'], \"\t\"), " + + "\"\n\")", json, "a\tb\tc\td\te\tf\n1\t2\t3\t4\t5\t6"); + } +}