diff --git a/include/libnftables/rule.h b/include/libnftables/rule.h
index 9989f19..3287c27 100644
--- a/include/libnftables/rule.h
+++ b/include/libnftables/rule.h
@@ -42,6 +42,7 @@ void nft_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nft_rule *t);
 enum {
 	NFT_RULE_O_DEFAULT	= 0,
 	NFT_RULE_O_XML,
+	NFT_RULE_O_JSON,
 };
 
 enum nft_rule_parse_type {
diff --git a/src/expr/bitwise.c b/src/expr/bitwise.c
index 9ebe3dc..eda452d 100644
--- a/src/expr/bitwise.c
+++ b/src/expr/bitwise.c
@@ -306,6 +306,37 @@ nft_rule_expr_bitwise_xml_parse(struct nft_rule_expr *e, char *xml)
 }
 
 static int
+nft_rule_expr_bitwise_snprintf_json(char *buf, size_t size,
+				   struct nft_expr_bitwise *bitwise)
+{
+	int len = size, offset = 0, ret;
+
+	ret = snprintf(buf, len, "\"sreg\" : %u, "
+				"\"dreg\" : %u, ",
+		       bitwise->sreg, bitwise->dreg);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, "\"mask\" : ");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_data_reg_snprintf(buf+offset, len, &bitwise->mask,
+				    NFT_RULE_O_JSON, 0, DATA_VALUE);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, ", \"xor\" : ");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_data_reg_snprintf(buf+offset, len, &bitwise->xor,
+				    NFT_RULE_O_JSON, 0, DATA_VALUE);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, "\"");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static int
 nft_rule_expr_bitwise_snprintf_xml(char *buf, size_t size,
 				   struct nft_expr_bitwise *bitwise)
 {
@@ -370,6 +401,8 @@ nft_rule_expr_bitwise_snprintf(char *buf, size_t size, uint32_t type,
 	struct nft_expr_bitwise *bitwise = (struct nft_expr_bitwise *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_bitwise_snprintf_json(buf, size, bitwise);
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_bitwise_snprintf_xml(buf, size, bitwise);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/expr/byteorder.c b/src/expr/byteorder.c
index 9d75824..4e0d9d7 100644
--- a/src/expr/byteorder.c
+++ b/src/expr/byteorder.c
@@ -285,6 +285,24 @@ err:
 }
 
 static int
+nft_rule_expr_byteorder_snprintf_json(char *buf, size_t size,
+				   struct nft_expr_byteorder *byteorder)
+{
+	int len = size, offset = 0, ret;
+
+	ret = snprintf(buf, len, "\"sreg\" : %u, "
+				 "\"dreg\" : %u, "
+				 "\"op\" : %u, "
+				 "\"len\" : %u, "
+				 "\"size\" : %u",
+		       byteorder->sreg, byteorder->dreg, byteorder->op,
+		       byteorder->len, byteorder->size);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static int
 nft_rule_expr_byteorder_snprintf_xml(char *buf, size_t size,
 				   struct nft_expr_byteorder *byteorder)
 {
@@ -329,6 +347,9 @@ nft_rule_expr_byteorder_snprintf(char *buf, size_t size, uint32_t type,
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_byteorder_snprintf_xml(buf, size,
 							    byteorder);
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_byteorder_snprintf_json(buf, size,
+							    byteorder);
 	default:
 		break;
 	}
diff --git a/src/expr/cmp.c b/src/expr/cmp.c
index 673f3e0..5d3ddeb 100644
--- a/src/expr/cmp.c
+++ b/src/expr/cmp.c
@@ -259,6 +259,25 @@ static int nft_rule_expr_cmp_xml_parse(struct nft_rule_expr *e, char *xml)
 }
 
 static int
+nft_rule_expr_cmp_snprintf_json(char *buf, size_t size, struct nft_expr_cmp *cmp)
+{
+	int len = size, offset = 0, ret;
+
+	ret = snprintf(buf, len, "\"sreg\" : %u, \"op\" : \"%s\", \"cmpdata\" : {",
+		       cmp->sreg, expr_cmp_str[cmp->op]);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = nft_data_reg_snprintf(buf+offset, len, &cmp->data,
+				    NFT_RULE_O_JSON, 0, DATA_VALUE);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, "}");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static int
 nft_rule_expr_cmp_snprintf_xml(char *buf, size_t size, struct nft_expr_cmp *cmp)
 {
 	int len = size, offset = 0, ret;
@@ -300,6 +319,8 @@ nft_rule_expr_cmp_snprintf(char *buf, size_t size, uint32_t type,
 {
 	struct nft_expr_cmp *cmp = (struct nft_expr_cmp *)e->data;
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_cmp_snprintf_json(buf, size, cmp);
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_cmp_snprintf_xml(buf, size, cmp);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/expr/counter.c b/src/expr/counter.c
index 129f32e..5e17bd9 100644
--- a/src/expr/counter.c
+++ b/src/expr/counter.c
@@ -193,6 +193,10 @@ nft_rule_expr_counter_snprintf(char *buf, size_t len, uint32_t type,
 	struct nft_expr_counter *ctr = (struct nft_expr_counter *)e->data;
 
 	switch(type) {
+
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"pkts\" : %lu, \"bytes\" : %lu",
+				ctr->pkts, ctr->bytes);
 	case NFT_RULE_O_XML:
 		return snprintf(buf, len, "<pkts>%lu</pkts><bytes>%lu</bytes>",
 				ctr->pkts, ctr->bytes);
diff --git a/src/expr/ct.c b/src/expr/ct.c
index 4042926..d6cde67 100644
--- a/src/expr/ct.c
+++ b/src/expr/ct.c
@@ -228,6 +228,11 @@ nft_rule_expr_ct_snprintf(char *buf, size_t len, uint32_t type,
 					  "<key>%u</key>"
 					  "<dir>%u</dir>",
 				ct->dreg, ct->key, ct->dir);
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"dreg\" : %u, "
+					  "\"key\" : %u, "
+					  "\"dir\" : %u",
+				ct->dreg, ct->key, ct->dir);
 	default:
 		break;
 	}
diff --git a/src/expr/data_reg.c b/src/expr/data_reg.c
index 5eb7f38..e5bd2fc 100644
--- a/src/expr/data_reg.c
+++ b/src/expr/data_reg.c
@@ -254,6 +254,42 @@ int nft_data_reg_xml_parse(union nft_data_reg *reg, char *xml)
 #endif
 }
 
+static int 
+nft_data_reg_value_snprintf_json(char *buf, size_t size,
+					   union nft_data_reg *reg,
+					   uint32_t flags)
+{
+	int len = size, offset = 0, ret, i, j;
+	uint8_t *tmp;
+	int data_len = reg->len/sizeof(uint32_t);
+
+	ret = snprintf(buf, len, "\"data_reg\": { \"type\" : \"value\", ");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, "\"len\" : %d, ", data_len);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	for (i=0; i<data_len; i++) {
+		ret = snprintf(buf+offset, len, "\"data%d\" : \"0x", i);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		tmp = (uint8_t *)&reg->val[i];
+
+		for (j=0; j<sizeof(int); j++) {
+			ret = snprintf(buf+offset, len, "%.02x", tmp[j]);
+			SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+		}
+
+		ret = snprintf(buf+offset, len, "\"");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	ret = snprintf(buf+offset, len, "}");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
 static
 int nft_data_reg_value_snprintf_xml(char *buf, size_t size,
 				    union nft_data_reg *reg, uint32_t flags)
@@ -309,6 +345,9 @@ int nft_data_reg_snprintf(char *buf, size_t size, union nft_data_reg *reg,
 	switch(reg_type) {
 	case DATA_VALUE:
 		switch(output_format) {
+		case NFT_RULE_O_JSON:
+			return nft_data_reg_value_snprintf_json(buf, size,
+							       reg, flags);
 		case NFT_RULE_O_XML:
 			return nft_data_reg_value_snprintf_xml(buf, size,
 							       reg, flags);
@@ -320,6 +359,11 @@ int nft_data_reg_snprintf(char *buf, size_t size, union nft_data_reg *reg,
 		}
 	case DATA_VERDICT:
 		switch(output_format) {
+		case NFT_RULE_O_JSON:
+			return snprintf(buf, size,
+					"\"data_reg\": { \"type\" : \"verdict\", "
+						"\"verdict\" : %d"
+					"}", reg->verdict);
 		case NFT_RULE_O_XML:
 			return snprintf(buf, size,
 					"<data_reg type=\"verdict\">"
@@ -332,6 +376,11 @@ int nft_data_reg_snprintf(char *buf, size_t size, union nft_data_reg *reg,
 		}
 	case DATA_CHAIN:
 		switch(output_format) {
+		case NFT_RULE_O_JSON:
+			return snprintf(buf, size,
+					"\"data_reg\": { \"type\" : \"chain\", "
+						"\"chain\" : %d"
+					"}", reg->verdict);
 		case NFT_RULE_O_XML:
 			return snprintf(buf, size,
 					"<data_reg type=\"chain\">"
diff --git a/src/expr/exthdr.c b/src/expr/exthdr.c
index fb3f7ad..5d564ec 100644
--- a/src/expr/exthdr.c
+++ b/src/expr/exthdr.c
@@ -262,6 +262,13 @@ nft_rule_expr_exthdr_snprintf(char *buf, size_t len, uint32_t type,
 	struct nft_expr_exthdr *exthdr = (struct nft_expr_exthdr *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"dreg\" : %u, "
+					  "\"type\" : %u, \"offset\" : %u, "
+					  "\"len\" : %u",
+					exthdr->dreg, exthdr->type,
+					exthdr->offset, exthdr->len);
+
 	case NFT_RULE_O_XML:
 		return snprintf(buf, len, "<dreg>%u</dreg>"
 					  "<type>%u</type><offset>%u</offset>"
diff --git a/src/expr/immediate.c b/src/expr/immediate.c
index 295054d..6a79476 100644
--- a/src/expr/immediate.c
+++ b/src/expr/immediate.c
@@ -295,6 +295,40 @@ nft_rule_expr_immediate_xml_parse(struct nft_rule_expr *e, char *xml)
 }
 
 static int
+nft_rule_expr_immediate_snprintf_json(char *buf, size_t len,
+				     struct nft_rule_expr *e, uint32_t flags)
+{
+	int size = len, offset = 0, ret;
+	struct nft_expr_immediate *imm = (struct nft_expr_immediate *)e->data;
+
+	ret = snprintf(buf, len, "\"dreg\" : %u, "
+				"\"immediatedata\" : {", imm->dreg);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+
+	if (e->flags & (1 << NFT_EXPR_IMM_DATA)) {
+		ret = nft_data_reg_snprintf(buf+offset, len, &imm->data,
+					    NFT_RULE_O_JSON, flags, DATA_VALUE);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	} else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) {
+		ret = nft_data_reg_snprintf(buf+offset, len, &imm->data,
+					  NFT_RULE_O_JSON, flags, DATA_VERDICT);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	} else if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) {
+		ret = nft_data_reg_snprintf(buf+offset, len, &imm->data,
+					    NFT_RULE_O_JSON, flags, DATA_CHAIN);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	ret = snprintf(buf+offset, len, "}");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static int
 nft_rule_expr_immediate_snprintf_xml(char *buf, size_t len,
 				     struct nft_rule_expr *e, uint32_t flags)
 {
@@ -362,6 +396,8 @@ nft_rule_expr_immediate_snprintf(char *buf, size_t len, uint32_t type,
 				 uint32_t flags, struct nft_rule_expr *e)
 {
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_immediate_snprintf_json(buf, len, e, flags);
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_immediate_snprintf_xml(buf, len, e, flags);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/expr/limit.c b/src/expr/limit.c
index 64a5b70..78798d6 100644
--- a/src/expr/limit.c
+++ b/src/expr/limit.c
@@ -194,6 +194,10 @@ nft_rule_expr_limit_snprintf(char *buf, size_t len, uint32_t type,
 		return snprintf(buf, len, "<rate>%"PRIu64"</rate>"
 					  "<depth>%"PRIu64"</depth>",
 				limit->rate, limit->depth);
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"rate\" : %"PRIu64", "
+					  "\"depth\" : %"PRIu64" ",
+				limit->rate, limit->depth);
 	default:
 		break;
 	}
diff --git a/src/expr/log.c b/src/expr/log.c
index 0e35a65..f7e7558 100644
--- a/src/expr/log.c
+++ b/src/expr/log.c
@@ -262,6 +262,13 @@ nft_rule_expr_log_snprintf(char *buf, size_t len, uint32_t type,
 					  "<qthreshold>%u</qthreshold>",
 				log->prefix, log->group,
 				log->snaplen, log->qthreshold);
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"prefix\" : \"%s\", "
+					  "\"group\" : %u, "
+					  "\"snaplen\" : %u, "
+					  "\"qthreshold\" : %u ",
+				log->prefix, log->group,
+				log->snaplen, log->qthreshold);
 	default:
 		break;
 	}
diff --git a/src/expr/lookup.c b/src/expr/lookup.c
index 6d2b9a2..ba56b78 100644
--- a/src/expr/lookup.c
+++ b/src/expr/lookup.c
@@ -228,6 +228,20 @@ nft_rule_expr_lookup_xml_parse(struct nft_rule_expr *e, char *xml)
 }
 
 static int
+nft_rule_expr_lookup_snprintf_json(char *buf, size_t size,
+				  struct nft_expr_lookup *l)
+{
+	int len = size, offset = 0, ret;
+
+	ret = snprintf(buf, len, "\"set\" : \"%s\", \"sreg\" : %u, \"dreg\" : %u",
+			l->set_name, l->sreg, l->dreg);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+
+static int
 nft_rule_expr_lookup_snprintf_xml(char *buf, size_t size,
 				  struct nft_expr_lookup *l)
 {
@@ -260,6 +274,8 @@ nft_rule_expr_lookup_snprintf(char *buf, size_t size, uint32_t type,
 	struct nft_expr_lookup *lookup = (struct nft_expr_lookup *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_lookup_snprintf_json(buf, size, lookup);
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_lookup_snprintf_xml(buf, size, lookup);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/expr/match.c b/src/expr/match.c
index edb78ea..8b98386 100644
--- a/src/expr/match.c
+++ b/src/expr/match.c
@@ -241,6 +241,29 @@ static int nft_rule_expr_match_xml_parse(struct nft_rule_expr *e, char *xml)
 #endif
 }
 
+static int nft_rule_expr_match_snprintf_json(char *buf, size_t len,
+					    struct nft_expr_match *mt)
+{
+	int ret, size=len;
+	int i;
+	int offset = 0;
+	uint8_t *data = (uint8_t *)mt->data;
+
+	ret = snprintf(buf, len, "\"name\" : \"%s\", \"rev\" : %u, \"info\" : 0x",
+				mt->name, mt->rev);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	for (i=0; i < mt->data_len; i++) {
+	        ret = snprintf(buf+offset, len, "%x", data[i] & 0xff);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	ret = snprintf(buf+offset, len, "\"");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
 static int nft_rule_expr_match_snprintf_xml(char *buf, size_t len,
 					    struct nft_expr_match *mt)
 {
@@ -272,6 +295,8 @@ nft_rule_expr_match_snprintf(char *buf, size_t len, uint32_t type,
 	struct nft_expr_match *match = (struct nft_expr_match *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_match_snprintf_json(buf, len, match);
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_match_snprintf_xml(buf, len, match);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/expr/meta.c b/src/expr/meta.c
index 6316a49..df0c2ee 100644
--- a/src/expr/meta.c
+++ b/src/expr/meta.c
@@ -196,6 +196,10 @@ nft_rule_expr_meta_snprintf(char *buf, size_t len, uint32_t type,
 	struct nft_expr_meta *meta = (struct nft_expr_meta *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"dreg\" : %u, "
+					  "\"key\" : %u",
+				meta->dreg, meta->key);
 	case NFT_RULE_O_XML:
 		return snprintf(buf, len, "<dreg>%u</dreg>"
 					  "<key>%u</key>",
diff --git a/src/expr/nat.c b/src/expr/nat.c
index 5d924cf..182448d 100644
--- a/src/expr/nat.c
+++ b/src/expr/nat.c
@@ -328,6 +328,48 @@ static int nft_rule_expr_nat_xml_parse(struct nft_rule_expr *e, char *xml)
 }
 
 static int
+nft_rule_expr_nat_snprintf_json(char *buf, size_t size,
+				struct nft_rule_expr *e)
+{
+	struct nft_expr_nat *nat = (struct nft_expr_nat *)e->data;
+	int len = size, offset = 0, ret = 0;
+
+	switch (nat->type) {
+	case NFT_NAT_SNAT:
+		ret = snprintf(buf, len,
+			"\"type\" : \"NFT_NAT_SNAT\", ");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+		break;
+	case NFT_NAT_DNAT:
+		ret = snprintf(buf, len,
+			"\"type\" : \"NFT_NAT_DNAT\", ");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+		break;
+	}
+
+	ret = snprintf(buf, len, "\"family\" : \"%s\", ",
+		       nat->family == AF_INET ? "AF_INET" : "AF_INET6");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	if (e->flags & (1 << NFT_EXPR_NAT_REG_ADDR_MIN)) {
+		ret = snprintf(buf, len, "\"sreg_addr_min_v4\" : %u, "
+				" \"sreg_addr_max_v4\" : %u, ",
+			       nat->sreg_addr_min, nat->sreg_addr_max);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	if (e->flags & (1 << NFT_EXPR_NAT_REG_PROTO_MIN)) {
+		ret = snprintf(buf, len, "\"sreg_proto_min\" : %u, "
+				"\"sreg_proto_max\" : %u",
+		       nat->sreg_proto_min, nat->sreg_proto_max);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	return offset;
+}
+
+
+static int
 nft_rule_expr_nat_snprintf_xml(char *buf, size_t size,
 				struct nft_rule_expr *e)
 {
@@ -412,6 +454,8 @@ nft_rule_expr_nat_snprintf(char *buf, size_t size, uint32_t type,
 			   uint32_t flags, struct nft_rule_expr *e)
 {
 	switch (type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_expr_nat_snprintf_json(buf, size, e);
 	case NFT_RULE_O_XML:
 		return nft_rule_expr_nat_snprintf_xml(buf, size, e);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/expr/payload.c b/src/expr/payload.c
index ecb1bce..4f114a6 100644
--- a/src/expr/payload.c
+++ b/src/expr/payload.c
@@ -257,6 +257,12 @@ nft_rule_expr_payload_snprintf(char *buf, size_t len, uint32_t type,
 	struct nft_expr_payload *payload = (struct nft_expr_payload *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return snprintf(buf, len, "\"dreg\" : %u, "
+					  "\"base\" : %u, \"offset\" : %u, "
+					  "\"len\" : %u ",
+				payload->dreg, payload->base,
+					payload->offset, payload->len);
 	case NFT_RULE_O_XML:
 		return snprintf(buf, len, "<dreg>%u</dreg>"
 					  "<base>%u</base><offset>%u</offset>"
diff --git a/src/expr/target.c b/src/expr/target.c
index 6652c47..f534cfe 100644
--- a/src/expr/target.c
+++ b/src/expr/target.c
@@ -245,6 +245,30 @@ nft_rule_expr_target_xml_parse(struct nft_rule_expr *e, char *xml)
 }
 
 static
+int nft_rule_exp_target_snprintf_json(char *buf, size_t len,
+				struct nft_expr_target *tg)
+{
+	int ret, size=len;
+	int i;
+	int offset = 0;
+	uint8_t *data = (uint8_t *)tg->data;
+
+	ret = snprintf(buf, len, "\"name\" : \"%s\",\"rev\" : %u,\"info\" : \"0x",
+			tg->name, tg->rev);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	for (i=0; i < tg->data_len; i++) {
+		ret = snprintf(buf+offset, len, "%x", data[i] & 0xff);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+	}
+
+	ret = snprintf(buf+offset, len, "\"");
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
+static
 int nft_rule_exp_target_snprintf_xml(char *buf, size_t len,
 				struct nft_expr_target *tg)
 {
@@ -275,6 +299,8 @@ nft_rule_expr_target_snprintf(char *buf, size_t len, uint32_t type,
 	struct nft_expr_target *target = (struct nft_expr_target *)e->data;
 
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_exp_target_snprintf_json(buf, len, target);
 	case NFT_RULE_O_XML:
 		return nft_rule_exp_target_snprintf_xml(buf, len, target);
 	case NFT_RULE_O_DEFAULT:
diff --git a/src/internal.h b/src/internal.h
index 769926b..8e2e26d 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -22,6 +22,7 @@
 #define NFT_RULE_XML_VERSION 0
 #define NFT_TABLE_JSON_VERSION 0
 #define NFT_CHAIN_JSON_VERSION 0
+#define NFT_RULE_JSON_VERSION 0
 
 struct expr_ops;
 
diff --git a/src/rule.c b/src/rule.c
index 4c1672d..34b9a27 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -665,6 +665,48 @@ int nft_rule_parse(struct nft_rule *r, enum nft_rule_parse_type type, char *data
 }
 EXPORT_SYMBOL(nft_rule_parse);
 
+static int nft_rule_snprintf_json(char *buf, size_t size, struct nft_rule *r,
+				 uint32_t type, uint32_t flags)
+{
+	int ret, len = size, offset = 0;
+	struct nft_rule_expr *expr;
+
+ret = snprintf(buf, size,
+		"{ \"rule\": { \"family\" : %u, \"table\" : \"%s\", "
+			"\"chain\"  : \"%s\", \"handle\" : %llu, \"version\" : %d, ",
+				r->family, r->table, r->chain,
+				(unsigned long long)r->handle,
+				NFT_RULE_JSON_VERSION);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, "\"rule_flags\" : %u, "
+					"\"compat_flags\" : %u, "
+					"\"compat_proto\" : %u, ",
+					r->rule_flags,
+					r->compat.flags, r->compat.proto);
+	SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	ret = snprintf(buf+offset, len, "\"expr\" : [");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	list_for_each_entry(expr, &r->expr_list, head) {
+		ret = snprintf(buf+offset, len,
+				" { \"type\" : \"%s\", ", expr->ops->name);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		ret = expr->ops->snprintf(buf+offset, len, type, flags, expr);
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+		ret = snprintf(buf+offset, len, "},");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	}
+	ret = snprintf(buf+offset-1, len, "]}}");
+		SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
+
+	return offset;
+}
+
 static int nft_rule_snprintf_xml(char *buf, size_t size, struct nft_rule *r,
 				 uint32_t type, uint32_t flags)
 {
@@ -731,6 +773,8 @@ int nft_rule_snprintf(char *buf, size_t size, struct nft_rule *r,
 		       uint32_t type, uint32_t flags)
 {
 	switch(type) {
+	case NFT_RULE_O_JSON:
+		return nft_rule_snprintf_json(buf, size, r, type, flags);
 	case NFT_RULE_O_XML:
 		return nft_rule_snprintf_xml(buf, size, r, type, flags);
 	case NFT_RULE_O_DEFAULT:
