diff --git a/README.md b/README.md
index 32301cf52..75640bf19 100644
--- a/README.md
+++ b/README.md
@@ -89,9 +89,11 @@ Usage example:
```
.filter(new AllureRestAssured())
```
-You can specify custom templateName:
+You can specify custom templates, which should be placed in src/main/resources/tpl folder:
```
-.filter(new AllureRestAssured().withTemplate("/templates/custom_template.ftl"))
+.filter(new AllureRestAssured()
+ .withRequestTemplate("custom-http-request.ftl")
+ .withResponseTemplate("custom-http-response.ftl"))
```
## OkHttp
@@ -136,3 +138,13 @@ Usage example:
.addInterceptorLast(new AllureHttpClientResponse());
```
+## JsonUnit
+JsonPatchMatcher is extension of JsonUnit matcher, that generates pretty html attachment for differences based on [json diff patch](https://github.com/benjamine/jsondiffpatch/blob/master/docs/deltas.md).
+
+```xml
+
+ io.qameta.allure
+ allure-jsonunit
+ $LATEST_VERSION
+
+```
\ No newline at end of file
diff --git a/allure-jsonunit/build.gradle b/allure-jsonunit/build.gradle
new file mode 100644
index 000000000..383fd829f
--- /dev/null
+++ b/allure-jsonunit/build.gradle
@@ -0,0 +1,13 @@
+description = 'Allure JsonUnit'
+
+apply from: "${gradleScriptDir}/maven-publish.gradle"
+apply from: "${gradleScriptDir}/bintray.gradle"
+apply plugin: 'maven'
+
+dependencies {
+ compile project(':allure-attachments')
+ compile('net.javacrumbs.json-unit:json-unit:2.0.0.RC1')
+
+ testCompile('junit:junit')
+ testCompile('org.hamcrest:hamcrest-library')
+}
diff --git a/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/AllureConfigurableJsonMatcher.java b/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/AllureConfigurableJsonMatcher.java
new file mode 100644
index 000000000..75cbd4020
--- /dev/null
+++ b/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/AllureConfigurableJsonMatcher.java
@@ -0,0 +1,26 @@
+package io.qameta.allure.jsonunit;
+
+import net.javacrumbs.jsonunit.core.Option;
+import net.javacrumbs.jsonunit.core.internal.Options;
+import org.hamcrest.Matcher;
+
+import java.math.BigDecimal;
+
+/**
+ * @param the type of matcher
+ * @see net.javacrumbs.jsonunit.ConfigurableJsonMatcher
+ */
+public interface AllureConfigurableJsonMatcher extends Matcher {
+
+ AllureConfigurableJsonMatcher withTolerance(BigDecimal tolerance);
+
+ AllureConfigurableJsonMatcher withTolerance(double tolerance);
+
+ AllureConfigurableJsonMatcher when(Option first, Option... next);
+
+ AllureConfigurableJsonMatcher withOptions(Options options);
+
+ AllureConfigurableJsonMatcher withMatcher(String matcherName, Matcher> matcher);
+
+ AllureConfigurableJsonMatcher whenIgnoringPaths(String... paths);
+}
diff --git a/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/DiffAttachment.java b/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/DiffAttachment.java
new file mode 100644
index 000000000..5af031a61
--- /dev/null
+++ b/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/DiffAttachment.java
@@ -0,0 +1,36 @@
+package io.qameta.allure.jsonunit;
+
+import io.qameta.allure.attachment.AttachmentData;
+
+/**
+ * @author Victor Orlovsky
+ */
+public class DiffAttachment implements AttachmentData {
+
+ private final String patch;
+ private final String actual;
+ private final String expected;
+
+ public DiffAttachment(final String actual, final String expected, final String patch) {
+ this.actual = actual;
+ this.expected = expected;
+ this.patch = patch;
+ }
+
+ public String getPatch() {
+ return patch;
+ }
+
+ public String getActual() {
+ return actual;
+ }
+
+ public String getExpected() {
+ return expected;
+ }
+
+ @Override
+ public String getName() {
+ return "JSON difference";
+ }
+}
diff --git a/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/JsonPatchListener.java b/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/JsonPatchListener.java
new file mode 100644
index 000000000..77dd38dfa
--- /dev/null
+++ b/allure-jsonunit/src/main/java/io/qameta/allure/jsonunit/JsonPatchListener.java
@@ -0,0 +1,144 @@
+package io.qameta.allure.jsonunit;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import net.javacrumbs.jsonunit.core.listener.Difference;
+import net.javacrumbs.jsonunit.core.listener.DifferenceContext;
+import net.javacrumbs.jsonunit.core.listener.DifferenceListener;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JsonUnit listener, that keeps difference and
+ * return formatted json to represent deltas
+ * (i.e. the output of jsondiffpatch.diff).
+ */
+public class JsonPatchListener implements DifferenceListener {
+
+ private static final String UNKNOWN_TYPE_ERROR = "Difference has unknown type";
+ private final List differences = new ArrayList<>();
+
+ private DifferenceContext context;
+
+ @Override
+ public void diff(final Difference difference, final DifferenceContext differenceContext) {
+ this.context = differenceContext;
+ differences.add(difference);
+ }
+
+ public List getDifferences() {
+ return differences;
+ }
+
+ public DifferenceContext getContext() {
+ return context;
+ }
+
+ @SuppressWarnings("ReturnCount")
+ private String getPath(final Difference difference) {
+ switch (difference.getType()) {
+ case DIFFERENT:
+ return difference.getActualPath();
+
+ case MISSING:
+ return difference.getExpectedPath();
+
+ case EXTRA:
+ return difference.getActualPath();
+
+ default:
+ throw new IllegalArgumentException(UNKNOWN_TYPE_ERROR);
+ }
+ }
+
+ @SuppressWarnings("ReturnCount")
+ private List