{"id":19764676,"url":"https://github.com/rey5137/jsonbatch","last_synced_at":"2025-08-04T05:35:23.807Z","repository":{"id":57722365,"uuid":"266571006","full_name":"rey5137/jsonbatch","owner":"rey5137","description":"An Engine to run batch request with JSON based REST APIs","archived":false,"fork":false,"pushed_at":"2020-07-09T03:21:10.000Z","size":218,"stargazers_count":14,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-30T14:33:34.772Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rey5137.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-24T15:36:26.000Z","updated_at":"2025-04-01T02:29:29.000Z","dependencies_parsed_at":"2022-08-29T23:01:47.110Z","dependency_job_id":null,"html_url":"https://github.com/rey5137/jsonbatch","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/rey5137/jsonbatch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rey5137%2Fjsonbatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rey5137%2Fjsonbatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rey5137%2Fjsonbatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rey5137%2Fjsonbatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rey5137","download_url":"https://codeload.github.com/rey5137/jsonbatch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rey5137%2Fjsonbatch/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268654909,"owners_count":24285124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-04T02:00:09.867Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T04:14:45.947Z","updated_at":"2025-08-04T05:35:23.784Z","avatar_url":"https://github.com/rey5137.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"JsonBatch\n=====================\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.rey5137/jsonbatch-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.rey5137/jsonbatch-core)\n[![Javadoc](https://www.javadoc.io/badge/com.github.rey5137/jsonbatch-core.svg)](http://www.javadoc.io/doc/com.github.rey5137/jsonbatch-core)\n\n**An Engine to run batch request with JSON based REST APIs**\n\nSome usecase for this library:\n- Provide a batch API to your REST API set.\n- Quickly roll out an adapter to migrate old API set.\n- Remove all boilerplate requests \u0026 responses classes.\n\nYou can try out JsonBatch via [this web app](https://jsonbatch-playground.herokuapp.com)\n\n* [Getting Started](#getting-started)\n* [How it work](#how-it-work)\n* [How it build JSON](#how-it-build-json)\n* [Data type](#data-type)\n* [Function](#function)\n* [Raw String](#raw-string)\n* [Object](#object)\n* [Array](#array)\n* [Where is the data](#where-is-the-data)\n* [A real example](#a-real-example)\n* [Custom function](#custom-function)\n* [Loop requests](#loop-requests)\n* [Response transform](#response-transform)\n* [Temporary variables](#temporary-variables)\n\n## Getting Started\n\nJsonBatch is available at the Central Maven Repository.\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecom.github.rey5137\u003c/groupId\u003e\n  \u003cartifactId\u003ejsonbatch-core\u003c/artifactId\u003e\n  \u003cversion\u003e1.3.2\u003c/version\u003e\n\u003c/dependency\u003e \n\n// need to include jsonpath dependency\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.jayway.jsonpath\u003c/groupId\u003e\n    \u003cartifactId\u003ejson-path\u003c/artifactId\u003e\n    \u003cversion\u003e2.4.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\nWe also need to add a sub package that implement RequestDispatcher. You can use this package that use Apache HttpClient:\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.github.rey5137\u003c/groupId\u003e\n        \u003cartifactId\u003ejsonbatch-apache-httpclient\u003c/artifactId\u003e\n        \u003cversion\u003e1.1.2\u003c/version\u003e\n    \u003c/dependency\u003e\n\n    // need to include httpclient dependency\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.apache.httpcomponents\u003c/groupId\u003e\n        \u003cartifactId\u003ehttpclient\u003c/artifactId\u003e\n        \u003cversion\u003e4.5.2\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\nOr this one use OkHttp:\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.github.rey5137\u003c/groupId\u003e\n        \u003cartifactId\u003ejsonbatch-okhttp\u003c/artifactId\u003e\n        \u003cversion\u003e1.1.2\u003c/version\u003e\n    \u003c/dependency\u003e\n\n    // need to include okhttp dependency\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.squareup.okhttp3\u003c/groupId\u003e\n        \u003cartifactId\u003eokhttp\u003c/artifactId\u003e\n        \u003cversion\u003e4.7.2\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n\n```\n\nJsonBatch depends on Jayway JsonPath library to parse json path.\n\nFirst we have to create a BatchEngine. Below is a simple example:\n```java\n  Configuration conf = Configuration.builder().build();\n  JsonBuilder jsonBuilder = new JsonBuilder(Functions.basic());\n  RequestDispatcher requestDispatcher = new ApacheHttpClientRequestDispatcher(HttpClients.createDefault());\n  BatchEngine batchEngine = new BatchEngine(conf, jsonBuilder, requestDispatcher);\n```\n\nBatchEngine has only 1 public method: \n```java\n  public Response execute(Request originalRequest, BatchTemplate template);\n```\n\nBy supplying the original request and a template, BatchEngine will construct \u0026 execute each request sequentially, then collect all responses and construct the final response.\n\n## How it work\nHere is Batch template full JSON format:\n```json\n{\n  \"requests\": [\n      {\n        \"predicate\": \"...\",\n        \"http_method\": \"...\",\n        \"url\": \"...\",\n        \"headers\": { ... },\n        \"body\": { ... },\n        \"requests\": [  ... \u003cnext requests\u003e ... ],\n        \"responses\": [ ... \u003cresponse templates\u003e ... ]\n      },\n      ...\n  ],\n  \"responses\": [\n      {\n        \"predicate\": \"...\",\n        \"status\": \"...\",\n        \"headers\": { ... },\n        \"body\": { ... }\n      },\n      ...\n  ],\n  \"dispatch_options\": {\n    \"fail_back_as_string\": ...,\n    \"ignore_parsing_error\": ...\n  },\n  \"loop_options\": {\n    \"max_loop_time\": ...\n  }\n}\n```  \nBy start, the Engine will loop though the **requests** list and choose the first template has predicate expression is true. \n(if a request template has predicate field is NULL, it will always be true).\nThe Engine will build request from template, pass it to **RequestDispatcher** to execute request and collect response. \n\nAfter that, it will find the first matching template from the responses list of current request template. \nIf it found a response template, it will stop execution chain, build and return the response. \nIf no matching response template found, the Engine will continue find next request from the requests list of current request template.\n\nAfter all requests are executed, the Engine will try to find a matching response template from responses list of BatchTemplate.\nIf a matching response template found, it will build the final response and return it.\nIf not, it will return a response contains all requests \u0026 responses it has collected so far.\n\nWhen **RequestDispatcher** execute a request, you can pass options via dispatch_options object to instruct it how to handle response:\n- fail_back_as_string: If RequestDispatcher cannot parse response body as JSON, it will return as String.\n- ignore_parsing_error: Ignore error when parsing response body, and return null instead.\n\n## How it build JSON\nTo know how to build a JSON object from template, JsonBatch use a JSON with special format. \nAll fields that aren't string will be same when build actual JSON but string field have to follow a specific format: \n\n**\\\u003cdata type\\\u003e \u003cjson_path or function(sum, min, max, ...) or raw_string\u003e**\n\nFor example:\n```json\n{\n  \"field_1\": \"int $.responses[0].body.field_a\" \n}\n```\nThe above template means: build a json object with \"field_1\" is integer, and extract value from json path \"$.responses[0].body.field_a\"\n\nYou can omit the **\\\u003cdata type\u003e** part like that:\n```json\n{\n  \"field_1\": \"$.responses[0].body.field_a\" \n}\n```\nJsonBatch will use the type of extracted value instead of casting it. \n\n\n## Data type\n| Type                     | Description      |\n| :----------------------- | :----------------|\n| str, string              | String           |\n| int, integer             | Integer          |\n| num, number              | Decimal          |\n| bool, boolean            | Boolean          |\n| obj, object              | Any object       |\n| str[], string[]          | String array     |\n| int[], integer[]         | Integer array    |\n| num[], number[]          | Decimal array    |\n| bool[], boolean[]        | Boolean array    |\n| obj[], object[]          | Any array        |\n \n In case the actual type of value is different with wanted type, the Engine will try to convert if possible. Some examples:\n \n\u003ctable\u003e\n\u003ctr\u003e \u003ctd\u003e Template \u003c/td\u003e \u003ctd\u003e Value \u003c/td\u003e \u003ctd\u003e Result \u003c/td\u003e \u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```json\n{\n    \"field_1\": \"int $.responses[0].body.field_a\"\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e \"10\" \u003c/td\u003e\n\u003ctd\u003e\n\n```json\n{\n    \"field_1\": 10\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\n\n```json\n{\n    \"field_1\": \"int[] $.responses[0].body.field_a\"\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e \"10\" \u003c/td\u003e\n\u003ctd\u003e\n\n```json\n{\n    \"field_1\": [ 10 ]\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\n\n```json\n{\n    \"field_1\": \"int $.responses[*].body.field_a\"\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e [\"9\", \"10\"] \u003c/td\u003e\n\u003ctd\u003e\n\n```json\n{\n    \"field_1\": 9\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n## Function\n Instead of extracting value from json path, we can use some function to aggregate value. \n The syntax is: **\\\u003cdata type\u003e \\_\\_\\\u003cfunction name\u003e(\\\u003cfunction arguments\u003e)** (Note that there is prefix \"\\_\\_\" before function name).\n \n Below is list of supported function:\n \n | Function   | Syntax                         | Example                             | Description                                    |\n | :--------- | :----------------------------- | :-----------------------------------| :--------------------------------------------- |\n | sum        | __sum(\\\u003carguments\u003e)      | __sum(\"$.responses[*].body.field_a\")      | Sum all values from int/decimal array          |\n | min        | __min(\\\u003carguments\u003e)      | __min(\"$.responses[*].body.field_a\")      | Get minimum value from int/decimal array       |\n | max        | __max(\\\u003carguments\u003e)      | __max(\"$.responses[*].body.field_a\")      | Get maximum value from int/decimal array       |\n | average    | __average(\\\u003carguments\u003e)  | __average(\"$.responses[*].body.field_a\")  | Calculate average value from int/decimal array |\n | and        | __and(\\\u003carguments\u003e)      | __and(\"$.responses[*].body.field_a\")      | And logical |\n | or         | __or(\\\u003carguments\u003e)       | __or(\"$.responses[*].body.field_a\")       | Or logical |\n | compare    | __cmp(\"\\\u003cexpression\u003e\")   | __cmp(\"@{$.field_a}@ \u003e 10\")               | Compare 2 value |\n | regex      | __regex(\"\u003cjson_path\u003e\", \"\\\u003cpattern\u003e\", \\\u003cindex\u003e)  | __regex(\"$.field_a\", \"(.*)\", 1)  | Extract from string by regex pattern and group index |\n \n ## Raw String\n For string field, instead of using JsonPath or Function, we can use raw string directly. \n Note that JsonBatch support inline variable with format: **@{\\\u003cschema\u003e}@**\n (You can use inline variable in both JSON key \u0026 value)\n \n Some examples:\n \u003ctable\u003e\n \u003ctr\u003e \u003ctd\u003e Template \u003c/td\u003e \u003ctd\u003e Result \u003c/td\u003e \u003c/tr\u003e\n \u003ctr\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"This is a raw string\"\n }\n ```\n \n \u003c/td\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"This is a raw string\"\n }\n ```\n \n \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003ctr\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"This is a raw string with @{$.key}@ var\"\n }\n ```\n \n \u003c/td\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"This is a raw string with 1 var\"\n }\n ```\n \n \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003ctr\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"This is a raw string with @{nested @{$.key}@}@ var\"\n }\n ```\n \n \u003c/td\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"This is a raw string with nested 1 var\"\n }\n ```\n \n \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003c/table\u003e\n \n \n ## Object\n When you define schema for each key in object, you often have to repeat a lot of JsonPath. \n To help reduce repeated works, you can add **__object_schema** key to define JSON context of current object. \n The root JsonPath ($) will point to new JSON context, and you can use **$$** at the start of JsonPath \n to point to grand JSON context.\n Below is two examples with 2 styles \u0026 same output:\n \n \u003ctable\u003e\n \u003ctr\u003e \u003ctd\u003e Template \u003c/td\u003e \u003ctd\u003e Value \u003c/td\u003e \u003ctd\u003e Result \u003c/td\u003e \u003c/tr\u003e\n \u003ctr\u003e\n \u003ctd\u003e\n \n ```json\n {\n    \"field_1\": \"int $.responses[0].body.field_a\",\n    \"field_2\": \"$.responses[0].body.field_b\"\n }\n ```\n \n \u003c/td\u003e\n \u003ctd\u003e\n \n ```json\n {\n    \"field_a\": \"10\",\n    \"field_b\": 1.5,\n     ...\n }\n ```\n \n \u003c/td\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": 10,\n     \"field_2\": 1.5\n }\n ```\n \n \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003ctr\u003e\n \u003ctd\u003e\n  \n  ```json\n  {\n     \"field_1\": \"$.field_a\",\n     \"field_2\": \"$.field_b\",\n     \"__object_schema\": \"$.responses[0].body\"\n  }\n  ```\n  \n  \u003c/td\u003e\n  \u003ctd\u003e\n  \n  ```json\n  {\n     \"field_a\": \"10\",\n     \"field_b\": 1.5,\n     ... \n  }\n  ```\n  \n  \u003c/td\u003e\n  \u003ctd\u003e\n  \n  ```json\n  {\n      \"field_1\": 10,\n      \"field_2\": 1.5\n  }\n  ```\n  \n  \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003c/table\u003e\n \n ## Array\n There is several way you can use to build Json Array:\n \n- String field with array data type. The engine will try to cast all array items to expected type.\n \n \u003ctable\u003e\n \u003ctr\u003e \u003ctd\u003e Template \u003c/td\u003e \u003ctd\u003e Data \u003c/td\u003e \u003ctd\u003e Result \u003c/td\u003e \u003c/tr\u003e\n \n \u003ctr\u003e\n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": \"int[] $[*].key_1\"\n }\n ```\n \n \u003c/td\u003e\n \n \u003ctd\u003e\n  \n ```json\n[\n  {\n    \"key_1\": \"1\"\n  },\n  {\n    \"key_1\": \"2\"\n  }\n]\n ```\n \n \u003c/td\u003e\n \n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": [1, 2]\n }\n ```\n \n \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003c/table\u003e\n \n- Use array field with string item. The engine will extract values from each child schema and merge all into 1 array. \n   \n \u003ctable\u003e\n \u003ctr\u003e \u003ctd\u003e Template \u003c/td\u003e \u003ctd\u003e Data \u003c/td\u003e \u003ctd\u003e Result \u003c/td\u003e \u003c/tr\u003e\n \n \u003ctr\u003e\n \u003ctd\u003e\n \n ```json\n {\n    \"field_1\": [\n      \"int[] $[*].key_1\",\n      \"int $[0].key_2\"\n    ]\n }\n ```\n \n \u003c/td\u003e\n \n \u003ctd\u003e\n  \n ```json\n[\n  {\n    \"key_1\": \"1\",\n    \"key_2\": 10\n  },\n  {\n    \"key_1\": \"2\"\n  }\n]\n ```\n \n \u003c/td\u003e\n \n \u003ctd\u003e\n \n ```json\n {\n     \"field_1\": [1, 2, 10]\n }\n ```\n \n \u003c/td\u003e\n \u003c/tr\u003e\n \n \u003c/table\u003e\n \n - Use array field with object item. Same as string item, but inside child item schema, \n you need to add **__array_schema** key to define JSON context of each child item. \n The root JsonPath ($) points to child item's JSON context. You can use **$$** at the start of JsonPath \n to points to grand JSON context.\n    \n  \u003ctable\u003e\n  \u003ctr\u003e \u003ctd\u003e Template \u003c/td\u003e \u003ctd\u003e Data \u003c/td\u003e \u003ctd\u003e Result \u003c/td\u003e \u003c/tr\u003e\n  \n  \u003ctr\u003e\n  \u003ctd\u003e\n  \n  ```json\n  {\n     \"field_1\": [\n        {\n          \"a\": \"int $.key_1\",\n          \"__array_schema\": \"$.array[*]\"\n        }   \n     ]\n  }\n  ```\n  \n  \u003c/td\u003e\n  \n  \u003ctd\u003e\n   \n  ```json\n{\n  \"array\": [\n    {\n      \"key_1\": \"1\",\n      \"key_2\": 10\n    },\n    {\n      \"key_1\": \"2\",\n      \"key_2\": 9\n    }\n  ],\n  \"other\": \"...\"\n}\n  ```\n  \n  \u003c/td\u003e\n  \n  \u003ctd\u003e\n  \n  ```json\n  {\n    \"field_1\": [ \n      {\n        \"a\": 1\n      },\n      {\n        \"a\": 2\n      }    \n    ]\n  }\n  ```\n  \n  \u003c/td\u003e\n  \u003c/tr\u003e\n  \n  \u003ctr\u003e\n  \u003ctd\u003e\n  \n  ```json\n  {\n     \"field_1\": [\n        {\n          \"a\": \"int $.key_1\",\n          \"b\": \"$$.array[@{$.key_1}@].key_2\",\n          \"__array_schema\": \"$.array[*]\"\n        }   \n     ]\n  }\n  ```\n  \n  \u003c/td\u003e\n    \n  \u003ctd\u003e\n   \n  ```json\n{\n  \"array\": [\n    {\n      \"key_1\": \"1\",\n      \"key_2\": 10\n    },\n    {\n      \"key_1\": \"0\",\n      \"key_2\": 9\n    }\n  ],\n  \"other\": \"...\"\n}\n  ```\n  \n  \u003c/td\u003e\n  \u003ctd\u003e\n  \n  ```json\n  {\n    \"field_1\": [ \n      {\n        \"a\": 1,\n        \"b\": 9\n      },\n      {\n        \"a\": 0,\n        \"b\": 10\n      }    \n    ]\n  }\n  ```\n  \n  \u003c/td\u003e      \n  \u003c/tr\u003e\n  \n  \n  \u003c/table\u003e\n \n ## Where is the data\n So far we know how to build the template, next is to understand where the data that engine extract from. \n So when BatchEngine execute a request, it will build a grand JSON that contains all original request, all the executed requests and responses.\n Below is format of this JSON:\n ```json\n{\n  \"original\": {\n    \"http_method\": \"...\",\n    \"url\": \"...\",\n    \"headers\": {\n      \"header_1\": [ \"...\" ],\n      \"header_2\": [ \"...\" ],\n      ...\n    },\n    \"body\": { ... }\n  },\n  \"requests\": [\n      {\n        \"http_method\": \"...\",\n        \"url\": \"...\",\n        \"headers\": {\n          \"header_1\": [ \"...\" ],\n          \"header_2\": [ \"...\" ],\n          ...\n        },\n        \"body\": { ... }\n      },\n      ...\n  ],\n  \"responses\": [\n      {\n        \"status\": ...,\n        \"headers\": {\n          \"header_1\": [ \"...\" ],\n          \"header_2\": [ \"...\" ],\n          ...\n        },\n        \"body\": { ... }\n      },\n      ...\n  ]\n}\n```  \n\n## A real example\nBelow is a real BatchTemplate example that work with **https://jsonplaceholder.typicode.com** REST API.\n```json\n{\n    \"requests\": [\n        {\n            \"http_method\": \"GET\",\n            \"url\": \"https://jsonplaceholder.typicode.com/posts\",\n            \"headers\": {\n                \"Accept\": \"str application/json, */*\"\n            },\n            \"body\": null,\n            \"requests\": [\n                {\n                    \"http_method\": \"GET\",\n                    \"url\": \"https://jsonplaceholder.typicode.com/posts/@{$.responses[0].body[0].id}@\",\n                    \"headers\": {\n                        \"Accept\": \"str application/json, */*\"\n                    },\n                    \"body\": null,\n                    \"requests\": [\n                        {\n                            \"http_method\": \"POST\",\n                            \"url\": \"https://jsonplaceholder.typicode.com/posts\",\n                            \"headers\": {\n                                \"Content-type\": \"str application/json; charset=UTF-8\"\n                            },\n                            \"body\": {\n                                \"title\": \"str A new post\",\n                                \"userId\": \"int $.responses[1].body.userId\",\n                                \"body\": \"str $.responses[1].body.body\"\n                            },\n                            \"responses\": [\n                                {\n                                    \"predicate\": \"__cmp(\\\"@{$.responses[2].status}@ != 201\\\")\",\n                                    \"status\": \"$.responses[2].status\",\n                                    \"headers\": null,\n                                    \"body\": {\n                                        \"first_post\": \"obj $.responses[1].body\",\n                                        \"new_post\": \"Error\"\n                                    }\n                                }\n                            ]\n                        }\n                    ]\n                }\n            ]\n        }\n    ],\n    \"responses\": [\n        {\n            \"status\": \"$.responses[2].status\",\n            \"headers\": null,\n            \"body\": {\n                \"first_post\": \"obj $.responses[1].body\",\n                \"new_post\": \"obj $.responses[2].body\"\n            }\n        }\n    ],\n    \"dispatch_options\": {\n        \"fail_back_as_string\": true,\n        \"ignore_parsing_error\": true\n    }\n}\n```\nLet me explain this template:\n- First, it will make a GET request to **https://jsonplaceholder.typicode.com/posts** to get list of a post.\n- Then, it extract the id of the first post, then make a second GET request to **https://jsonplaceholder.typicode.com/posts/{id}** to get the post details.\n- After that, it will make a POST request to **https://jsonplaceholder.typicode.com/posts** to create a new post with userId \u0026 body are same as first post.\n- If the POST request succeed, it will return a response with both first post \u0026 new post. If not (status != 201), it will return a response with new_post = \"Error\". \n\nYou can try out this example via [this web app](https://jsonbatch-playground.herokuapp.com/sample1)\n\n## Custom function\nYou can add your own custom function to JsonBuilder object. All the functions have to extend from a base class **Function**:\n```java\npublic abstract class Function {\n\n    public abstract String getName();\n\n    public abstract boolean isReduceFunction();\n\n    public Object invoke(Type type, List\u003cObject\u003e arguments) {\n        return null;\n    }\n\n    public Result handle(Type type, Object argument, Result prevResult) {\n        return null;\n    }\n}\n```\nThere is 2 abstract method you will have to override:\n- **getName()**: return the unique name of your function.\n- **isReduceFunction()**: define that your function is reduce function or not. \n\nIf your function is a reduce function, then you have to override the **handle()** method also. For reduce function, JsonBuilder will pass argument one by one, along with previous Result object (note that for first time it call **handle()** method, prevResult will be null)\n\nIf your function isn't a reduce function, then have to override the **invoke()** method, and JsonBuilder will call this method only one time with all the argument list.\n\nNote that the **type** argument is the expected type of function result, and it can be null. \n\nYou can also use this package that provide extra functions to JsonBatch:\n```xml\n\u003cdependencies\u003e\n    \u003cdependency\u003e\n        \u003cgroupId\u003ecom.github.rey5137\u003c/groupId\u003e\n        \u003cartifactId\u003ejsonbatch-functions\u003c/artifactId\u003e\n        \u003cversion\u003e1.0.0\u003c/version\u003e\n    \u003c/dependency\u003e\n\n    // need this dependency for BeanShellFunction\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.apache-extras.beanshell\u003c/groupId\u003e\n        \u003cartifactId\u003ebsh\u003c/artifactId\u003e\n        \u003cversion\u003e2.0b6\u003c/version\u003e\n    \u003c/dependency\u003e\n\n    // need this dependency for GroovyFunction\n\n    \u003cdependency\u003e\n        \u003cgroupId\u003eorg.codehaus.groovy\u003c/groupId\u003e\n        \u003cartifactId\u003egroovy\u003c/artifactId\u003e\n        \u003cversion\u003e2.5.12\u003c/version\u003e\n    \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\n## Loop requests\nThere is a case you want to loop some request until you found an expected response. JsonBatch also support this.\nBelow is an example template:\n```json\n{\n    \"requests\": [\n        {\n            \"loop\": {\n                \"counter_init\": 0,\n                \"counter_predicate\": \"__cmp(\\\"@{$.requests[0].counter}@ \u003c 3\\\")\",\n                \"counter_update\": \"$.requests[0].times.length()\",\n                \"requests\": [\n                    {\n                        \"predicate\": \"...\",\n                        \"http_method\": \"GET\",\n                        \"url\": \"https://test.com/@{$.requests[0].counter}@\",\n                        \"body\": {}\n                    },\n                    ...\n                ]\n            }\n        }\n    ],\n    \"loop_options\": {\n        \"max_loop_time\": 10\n    }\n}\n```\nAs you can see, inside the first request template, we have a new object **loop** to define the loop request.\n- **counter_init**: A schema to initiate a counter object for your loop request. The counter object will be stored and can be accessed later via JsonPath.\n- **counter_predicate**: A predicate schema that should return Boolean object. The loop will run as long as this predicate return true.\n- **counter_update**: A schema to update the counter object each time the loop run.\n- **requests**: A list of request template will be executed each time.\n\nNext is the sample Batch response JSON for above template:\n```json\n{\n  \"original\": {},\n  \"requests\": [\n    {\n      \"times\": [\n        [ {\n            \"http_method\": \"GET\",\n            \"url\": \"https://test.com/0\",\n            \"headers\": {},\n            \"body\": {}\n        } ],\n        [ {\n            \"http_method\": \"GET\",\n            \"url\": \"https://test.com/1\",\n            \"headers\": {},\n            \"body\": {}\n        } ],\n        [ {\n            \"http_method\": \"GET\",\n            \"url\": \"https://test.com/2\",\n            \"headers\": {},\n            \"body\": {}\n        } ]\n      ],\n      \"counter\": 3\n    }\n  ],\n  \"responses\": [\n      {\n        \"times\": [\n          [ {\n              \"status\": 200,\n              \"headers\": {},\n              \"body\": {}\n          } ],\n          [ {\n              \"status\": 200,\n              \"headers\": {},\n              \"body\": {}\n          } ],\n          [ {\n              \"status\": 200,\n              \"headers\": {},\n              \"body\": {}\n          } ]\n        ]\n      }\n  ]\n}\n```\n\nThe structure of the loop request is different with single request:\n- **times**: An array contains requests of each loop time. \n- **counter**: the counter object of loop request.\n\nThe same structure also applied to loop response.\n\nNote that loop request is powerful feature, but also can be misconfigured easily, that lead to an endless loop. \nTo avoid this issue, JsonBatch use a config **max_loop_time**  (default is 10). \nIf a loop ran too many times and surpassed this config, the Engine will forcefully break the loop.\n\n## Response transform\nBy default, the Engine will put all the response data into the grand JSON. \nBut if you want to only keep some interested data and discard the rest of the response (to make it more memory-friendly),\nthen you can supply a list of transformers inside the request template. \n```json\n{\n  \"requests\": [\n      {\n        \"predicate\": \"...\",\n        \"http_method\": \"...\",\n        \"url\": \"...\",\n        \"headers\": { ... },\n        \"body\": { ... },\n        \"transformers\": [\n          {\n            \"predicate\": \"...\",\n            \"status\": \"...\",\n            \"headers\": { ... },\n            \"body\": { ... }\n          },\n          ...\n        ],\n        \"requests\": [  ... \u003cnext requests\u003e ... ],\n        \"responses\": [ ... \u003cresponse templates\u003e ... ]\n      },\n      ...\n  ]\n  ...\n}\n```  \nThe transformer template is same as response template, the only different is the JSON object it'll work on (the root level of JsonPath will be different). \nTransformer template works on each corresponding JSON response, but response template works on the grand JSON that constains all data. \n\n## Temporary Variables\nIn case you want to store some temporary variables, you can define **vars templates** inside request template. \n```json\n{\n  \"requests\": [\n      {\n        \"predicate\": \"...\",\n        \"http_method\": \"...\",\n        \"url\": \"...\",\n        \"headers\": { ... },\n        \"body\": { ... },\n        \"vars\": [\n          {\n            \"predicate\": \"...\",\n            \"vars\": {\n              \"var_1\": \"...\",\n              \"var_2\": \"...\",\n              \"...\"\n            }\n          }\n        ],\n        ...\n      },\n      ...\n  ]\n  ...\n}\n```  \nAfter executing request (and transform response if possible), the Engine will loop though each vars template and evaluate its predicate.\nIf the template predicate is true, it will build vars object and merge result with **vars** object of grand JSON:\n```json\n{\n  \"original\": {...},\n  \"requests\": [...],\n  \"responses\": [...],\n  \"vars\": {\n    \"var_1\": \"...\",\n    \"var_2\": \"...\",\n    ...\n  }\n}\n```  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frey5137%2Fjsonbatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frey5137%2Fjsonbatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frey5137%2Fjsonbatch/lists"}