Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/com/dashjoin/jsonata/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,13 @@ static void string(StringBuilder b, Object arg, boolean prettify, String indent)
b.append('{');
if (prettify)
b.append('\n');
for (Entry<String, Object> e : ((Map<String, Object>) arg).entrySet()) {
for (Entry<Object, Object> e : ((Map<Object, Object>) arg).entrySet()) {
if (prettify) {
b.append(indent);
b.append(" ");
}
b.append('"');
Utils.quote(e.getKey(), b);
Utils.quote(e.getKey().toString(), b);
b.append('"');
b.append(':');
if (prettify)
Expand Down
22 changes: 19 additions & 3 deletions src/main/java/com/dashjoin/jsonata/Jsonata.java
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,14 @@ static Symbol chainAST() {
List args = new ArrayList<>(); args.add(lhs); args.add(func); // == [lhs, func]
result = /* await */ apply(chain, args, null, environment);
} else {
Object procName = getProcName(expr.rhs);
if ("each".equals(procName) && lhs instanceof List && "partial".equals(expr.rhs.type)) {
var tempLhs = (List)lhs;
lhs = new HashMap();
for (int i = 0; i < tempLhs.size(); i++) {
((HashMap) lhs).put(i, tempLhs.get(i));
}
Copy link
Author

@sergeevik sergeevik Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe better:

var lhsNewMap =  new HashMap();
var lhsOldList = (List)lhs;
for (int i = 0; i < lhsOldList.size(); i++) {
  lhsNewMap.put(i, tempLhs.get(i));
}
lhs = lhsNewMap;

}
List args = new ArrayList<>(); args.add(lhs); // == [lhs]
result = /* await */ apply(func, args, null, environment);
}
Expand All @@ -1544,6 +1552,14 @@ static Symbol chainAST() {
return result;
}

String getProcName(Symbol rhs) {
Symbol procedure = rhs.procedure;
if (procedure == null) return null;
Object procName = procedure.value;
if (procName == null) return null;
return procName.toString();
}

boolean isFunctionLike(Object o) {
return Utils.isFunction(o) || Functions.isLambda(o) || (o instanceof Pattern);
}
Expand Down Expand Up @@ -1755,7 +1771,7 @@ Jsonata getPerThreadInstance() {
List _res = new ArrayList<>();
for (String s : (List<String>)validatedArgs) {
//System.err.println("PAT "+proc+" input "+s);
if (((Pattern)proc).matcher(s).find()) {
if (s != null && ((Pattern)proc).matcher(s).find()) {
//System.err.println("MATCH");
_res.add(s);
}
Expand Down Expand Up @@ -1821,7 +1837,7 @@ Object evaluateLambda(Symbol expr, Object input, Frame environment) {
var evaluatedArgs = new ArrayList<>();
for(var ii = 0; ii < expr.arguments.size(); ii++) {
var arg = expr.arguments.get(ii);
if (arg.type.equals("operator") && (arg.value.equals("?"))) {
if (arg.type.equals("operator") && (arg.value.toString().equals("?"))) {
evaluatedArgs.add(arg);
} else {
evaluatedArgs.add(/* await */ evaluate(arg, input, environment));
Expand Down Expand Up @@ -1910,7 +1926,7 @@ Object partialApplyProcedure(Symbol proc, List<Symbol> args) {
for (var param : proc.arguments) {
// proc.arguments.forEach(Object (param, index) {
Object arg = index<args.size() ? args.get(index) : null;
if ((arg==null) || (arg instanceof Symbol && ("operator".equals(((Symbol)arg).type) && "?".equals(((Symbol)arg).value)))) {
if ((arg==null) || (arg instanceof Symbol && ("operator".equals(((Symbol)arg).type) && (((Symbol)arg).value.toString().equals("?"))))) {
unboundArgs.add(param);
} else {
env.bind((String)param.value, arg);
Expand Down
29 changes: 29 additions & 0 deletions src/test/java/com/dashjoin/jsonata/CustomFunctionTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dashjoin.jsonata;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -85,4 +86,32 @@ public Object call(Object input, @SuppressWarnings("rawtypes") List args) throws
Assertions.assertEquals("T0410", ex.getError());
Assertions.assertEquals("append", ex.getExpected());
}

@Test
public void testEachEmptyArray() {
var expression = Jsonata.jsonata("[] ~> $each(?, function($v) { $v })");
Object evaluate = expression.evaluate("{}");
Assertions.assertNull(evaluate);
}

@Test
public void testEachArrayWithData() {
var expression = Jsonata.jsonata("[123, 321] ~> $each(?, function($v) { $v })");
Object evaluate = expression.evaluate("{}");
Assertions.assertInstanceOf(List.class, evaluate);
List<Integer> expected = List.of(123, 321);
Assertions.assertEquals(expected, evaluate);
}

@Test
public void testMapInPipe() throws IOException {
Object data = JsonUtils.readJsonFromResources("map_in_pipe/input.json");
Object answer = JsonUtils.readJsonFromResources("map_in_pipe/answer.json");
var expression = Jsonata.jsonata("$sift($.buildings, function($v, $k) { $k ~> /o/ and $v.presentation.*.widget})" +
" ~> $each(?, function($v, $key) {{ $key: $sift($v.presentation, function($v, $k){ $v.widget }) }})" +
" ~> $map(?, function($v){($v.*)})");
Object evaluate = expression.evaluate(data);
Assertions.assertInstanceOf(List.class, evaluate);
Assertions.assertEquals(answer, evaluate);
}
}
2 changes: 1 addition & 1 deletion src/test/java/com/dashjoin/jsonata/Generate.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static void main(String[] args) throws IOException {
String name = cas.getName().substring(0, cas.getName().length() - 5);
String jname = name.replace('-', '_');

Object jsonCase = new JsonataTest().readJson(cas.getAbsolutePath());
Object jsonCase = JsonUtils.readJson(cas.getAbsolutePath());
if (jsonCase instanceof List) {
for (int i=0; i<((List)jsonCase).size(); i++) {
b.append("// " + s(((Map)((List) jsonCase).get(i)).get("expr"))+"\n");
Expand Down
34 changes: 34 additions & 0 deletions src/test/java/com/dashjoin/jsonata/JsonUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.dashjoin.jsonata;

import com.dashjoin.jsonata.json.Json;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.JsonMappingException;

import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

public class JsonUtils {

static Charset utf8Charset = Charset.forName("UTF-8");

static Object toJson(String jsonStr) throws JsonMappingException, JsonProcessingException {
return Json.parseJson(jsonStr);
}

static Object readJson(String name) throws StreamReadException, DatabindException, IOException {
return Json.parseJson(new FileReader(name, utf8Charset));
}

static Object readJsonFromResources(String name) throws IOException {
InputStream resourceAsStream = JsonUtils.class.getClassLoader().getResourceAsStream(name);
if (resourceAsStream == null) {
throw new RuntimeException("Resource not found: " + name);
}
return Json.parseJson(new InputStreamReader(resourceAsStream, utf8Charset));
}
}
22 changes: 4 additions & 18 deletions src/test/java/com/dashjoin/jsonata/JsonataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,6 @@ ObjectMapper getObjectMapper() {
return om;
}

Object toJson(String jsonStr) throws JsonMappingException, JsonProcessingException {
//ObjectMapper om = getObjectMapper();
//Object json = om.readValue(jsonStr, Object.class);
Object json = Json.parseJson(jsonStr);
return json;
}

Object readJson(String name) throws StreamReadException, DatabindException, IOException {
//ObjectMapper om = getObjectMapper();
//Object json = om.readValue(new java.io.FileReader(name, Charset.forName("UTF-8")), Object.class);

Object json = Json.parseJson(new java.io.FileReader(name, Charset.forName("UTF-8")));
return json;
}

@Test
public void testSimple() {
Expand All @@ -125,7 +111,7 @@ public void testSimple() {

@Test
public void testPath() throws Exception {
Object data = readJson("jsonata/test/test-suite/datasets/dataset0.json");
Object data = JsonUtils.readJson("jsonata/test/test-suite/datasets/dataset0.json");
System.out.println(data);
testExpr("foo.bar", data, null, 42,null);
}
Expand All @@ -146,7 +132,7 @@ public void runCase(String name) throws Exception {
}

public void runSubCase(String name, int subNr) throws Exception {
List cases = (List)readJson(name);
List cases = (List)JsonUtils.readJson(name);
if (!runTestCase(name+"_"+subNr, (Map<String, Object>) cases.get(subNr)))
throw new Exception();
}
Expand All @@ -158,7 +144,7 @@ boolean runTestSuite(String name) throws Exception {

boolean success = true;

Object testCase = readJson(name);
Object testCase = JsonUtils.readJson(name);
if (testCase instanceof List) {
// some cases contain a list of test cases
// loop over the case definitions
Expand Down Expand Up @@ -264,7 +250,7 @@ boolean runTestCase(String name, Map<String, Object> testDef) throws Exception {

Object data = testDef.get("data");
if (data==null && dataset!=null)
data = readJson("jsonata/test/test-suite/datasets/"+dataset+".json");
data = JsonUtils.readJson("jsonata/test/test-suite/datasets/"+dataset+".json");

TestOverride to = getOverrideForTest(name);
if (to!=null) {
Expand Down
30 changes: 30 additions & 0 deletions src/test/resources/map_in_pipe/answer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"first": {
"widget": {
"data": "home widget"
}
}
},
{
"first": {
"widget": {
"data": "work widget"
}
}
},
{
"first": {
"widget": {
"data": "old widget"
}
}
},
{
"first": {
"widget": {
"data": "operation widget"
}
}
}
]
58 changes: 58 additions & 0 deletions src/test/resources/map_in_pipe/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"buildings": {
"home": {
"address": "home address",
"period": "11 month on 12",
"presentation": {
"first": {
"widget": {
"data": "home widget"
}
}
}
},
"work": {
"address": "work address",
"presentation": {
"first": {
"widget": {
"data": "work widget"
}
}
}
},
"vacancy": {
"address": "vacancy address",
"period": "1 month on 12",
"presentation": {
"first": {
"widget": {
"data": "vacancy widget"
}
}
}
},
"old": {
"address": "old address",
"period": "2 month on 12",
"presentation": {
"first": {
"widget": {
"data": "old widget"
}
}
}
},
"operation": {
"address": "operation address",
"period": "3 month on 12",
"presentation": {
"first": {
"widget": {
"data": "operation widget"
}
}
}
}
}
}