diff --git a/cmds/qgroup.c b/cmds/qgroup.c
index 94721e2d..9fe692a2 100644
--- a/cmds/qgroup.c
+++ b/cmds/qgroup.c
@@ -35,6 +35,7 @@
 #include "common/help.h"
 #include "common/units.h"
 #include "common/parse-utils.h"
+#include "common/format-output.h"
 #include "common/messages.h"
 #include "cmds/commands.h"
 #include "cmds/qgroup.h"
@@ -1433,6 +1434,80 @@ static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
 	}
 }
 
+static const struct rowspec qgroup_show_rowspec[] = {
+	{ .key = "qgroupid", .fmt = "qgroupid", .out_json = "qgroupid" },
+	{ .key = "referenced", .fmt = "%llu", .out_json = "referenced" },
+	{ .key = "exclusive", .fmt = "%llu", .out_json = "exclusive" },
+	{ .key = "max_referenced", .fmt = "size", .out_json = "max_referenced" },
+	/* Special value if limits not set. */
+	{ .key = "max_referenced-none", .fmt = "%s", .out_json = "max_referenced" },
+	{ .key = "max_exclusive", .fmt = "size", .out_json = "max_exclusive" },
+	/* Special value if limits not set. */
+	{ .key = "max_exclusive-none", .fmt = "%s", .out_json = "max_exclusive" },
+	{ .key = "path", .fmt = "%s", .out_json = "path" },
+	{ .key = "parents", .fmt = "list", .out_json = "parents" },
+	{ .key = "children", .fmt = "list", .out_json = "children" },
+	/* Workaround for printing qgroupid in the list as a plain value */
+	{ .key = "qgroupid-list", .fmt = "qgroupid", .out_json = NULL },
+	ROWSPEC_END
+};
+
+static void print_all_qgroups_json(struct qgroup_lookup *qgroup_lookup)
+{
+	struct rb_node *n;
+	struct btrfs_qgroup *qgroup;
+	struct format_ctx fctx;
+
+	fmt_start(&fctx, qgroup_show_rowspec, 24, 0);
+	fmt_print_start_group(&fctx, "qgroup-show", JSON_TYPE_ARRAY);
+
+	n = rb_first(&qgroup_lookup->root);
+	while (n) {
+		struct btrfs_qgroup_list *list = NULL;
+
+		qgroup = rb_entry(n, struct btrfs_qgroup, sort_node);
+
+		fmt_print_start_group(&fctx, NULL, JSON_TYPE_MAP);
+
+		fmt_print(&fctx, "qgroupid",
+				btrfs_qgroup_level(qgroup->qgroupid),
+				btrfs_qgroup_subvolid(qgroup->qgroupid));
+		fmt_print(&fctx, "referenced", qgroup->info.rfer);
+		if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_RFER)
+			fmt_print(&fctx, "max_referenced", qgroup->limit.max_rfer);
+		else
+			fmt_print(&fctx, "max_referenced-none", "none");
+		fmt_print(&fctx, "exclusive", qgroup->info.excl);
+		if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_EXCL)
+			fmt_print(&fctx, "max_exclusive", qgroup->limit.max_excl);
+		else
+			fmt_print(&fctx, "max_exclusive-none", "none");
+		fmt_print(&fctx, "path", qgroup->path ?: "");
+
+		fmt_print_start_group(&fctx, "parents", JSON_TYPE_ARRAY);
+		list_for_each_entry(list, &qgroup->qgroups, next_qgroup) {
+			fmt_print(&fctx, "qgroupid-list",
+				btrfs_qgroup_level(list->qgroup->qgroupid),
+				btrfs_qgroup_subvolid(list->qgroup->qgroupid));
+		}
+		fmt_print_end_group(&fctx, "parents");
+
+		fmt_print_start_group(&fctx, "children", JSON_TYPE_ARRAY);
+		list_for_each_entry(list, &qgroup->members, next_member) {
+			fmt_print(&fctx, "qgroupid-list",
+				btrfs_qgroup_level(list->member->qgroupid),
+				btrfs_qgroup_subvolid(list->member->qgroupid));
+		}
+		fmt_print_end_group(&fctx, "children");
+
+		fmt_print_end_group(&fctx, NULL);
+
+		n = rb_next(n);
+	}
+	fmt_print_end_group(&fctx, "qgroup-show");
+	fmt_end(&fctx);
+}
+
 static int show_qgroups(int fd,
 		       struct btrfs_qgroup_filter_set *filter_set,
 		       struct btrfs_qgroup_comparer_set *comp_set)
@@ -1447,7 +1522,10 @@ static int show_qgroups(int fd,
 		return ret;
 	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
 				  filter_set, comp_set);
-	print_all_qgroups(&sort_tree);
+	if (bconf.output_format == CMD_FORMAT_JSON)
+		print_all_qgroups_json(&sort_tree);
+	else
+		print_all_qgroups(&sort_tree);
 
 	__free_all_qgroups(&qgroup_lookup);
 	return ret;
@@ -1855,6 +1933,8 @@ static const char * const cmd_qgroup_show_usage[] = {
 	"               you can use '+' or '-' in front of each item.",
 	"               (+:ascending, -:descending, ascending default)",
 	"--sync         force sync of the filesystem before getting info",
+	HELPINFO_INSERT_GLOBALS,
+	HELPINFO_INSERT_FORMAT,
 	NULL
 };
 
@@ -1974,7 +2054,7 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
 out:
 	return !!ret;
 }
-static DEFINE_SIMPLE_COMMAND(qgroup_show, "show");
+static DEFINE_COMMAND_WITH_FLAGS(qgroup_show, "show", CMD_FORMAT_JSON);
 
 static const char * const cmd_qgroup_limit_usage[] = {
 	"btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
diff --git a/common/format-output.c b/common/format-output.c
index c194a23c..c6ab3bf4 100644
--- a/common/format-output.c
+++ b/common/format-output.c
@@ -272,7 +272,8 @@ void fmt_print(struct format_ctx *fctx, const char* key, ...)
 		} else {
 			/* Simple key/values */
 			fmt_separator(fctx);
-			printf("\"%s\": ", row->out_json);
+			if (row->out_json)
+				printf("\"%s\": ", row->out_json);
 		}
 	}