diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 232636cde21..d2524e34f58 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -6073,7 +6073,7 @@ dump_analyzer_json (const supergraph &sg,
   toplev_obj->set ("egraph", eg.to_json ());
 
   pretty_printer pp;
-  toplev_obj->print (&pp);
+  toplev_obj->print (&pp, flag_diagnostics_json_formatting);
   pp_formatted_text (&pp);
 
   delete toplev_obj;
diff --git a/gcc/common.opt b/gcc/common.opt
index 161a035d736..cb82272e31d 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1391,6 +1391,10 @@ Enum(diagnostic_color_rule) String(always) Value(DIAGNOSTICS_COLOR_YES)
 EnumValue
 Enum(diagnostic_color_rule) String(auto) Value(DIAGNOSTICS_COLOR_AUTO)
 
+fdiagnostics-json-formatting
+Common Var(flag_diagnostics_json_formatting) Init(1)
+Enable formatting of JSON output.
+
 fdiagnostics-urls=
 Driver Common Joined RejectNegative Var(flag_diagnostics_show_urls) Enum(diagnostic_url_rule) Init(DIAGNOSTICS_URL_AUTO)
 -fdiagnostics-urls=[never|always|auto]	Embed URLs in diagnostics.
diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index 418db74522d..c013192de06 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -56,11 +56,13 @@ public:
   }
 
 protected:
-  json_output_format (diagnostic_context &context)
+  json_output_format (diagnostic_context &context,
+		      bool formatted)
   : diagnostic_output_format (context),
     m_toplevel_array (new json::array ()),
     m_cur_group (nullptr),
-    m_cur_children_array (nullptr)
+    m_cur_children_array (nullptr),
+    m_formatted (formatted)
   {
   }
 
@@ -68,7 +70,7 @@ protected:
   void
   flush_to_file (FILE *outf)
   {
-    m_toplevel_array->dump (outf);
+    m_toplevel_array->dump (outf, m_formatted);
     fprintf (outf, "\n");
     delete m_toplevel_array;
     m_toplevel_array = nullptr;
@@ -84,6 +86,8 @@ private:
   /* The JSON array for the "children" array within the current diagnostic
      group.  */
   json::array *m_cur_children_array;
+
+  bool m_formatted;
 };
 
 /* Generate a JSON object for LOC.  */
@@ -301,8 +305,9 @@ json_output_format::on_end_diagnostic (const diagnostic_info &diagnostic,
 class json_stderr_output_format : public json_output_format
 {
 public:
-  json_stderr_output_format (diagnostic_context &context)
-  : json_output_format (context)
+  json_stderr_output_format (diagnostic_context &context,
+			     bool formatted)
+    : json_output_format (context, formatted)
   {
   }
   ~json_stderr_output_format ()
@@ -315,8 +320,9 @@ class json_file_output_format : public json_output_format
 {
 public:
   json_file_output_format (diagnostic_context &context,
+			   bool formatted,
 			   const char *base_file_name)
-  : json_output_format (context),
+  : json_output_format (context, formatted),
     m_base_file_name (xstrdup (base_file_name))
   {
   }
@@ -367,10 +373,12 @@ diagnostic_output_format_init_json (diagnostic_context *context)
 /* Populate CONTEXT in preparation for JSON output to stderr.  */
 
 void
-diagnostic_output_format_init_json_stderr (diagnostic_context *context)
+diagnostic_output_format_init_json_stderr (diagnostic_context *context,
+					   bool formatted)
 {
   diagnostic_output_format_init_json (context);
-  context->set_output_format (new json_stderr_output_format (*context));
+  context->set_output_format (new json_stderr_output_format (*context,
+							     formatted));
 }
 
 /* Populate CONTEXT in preparation for JSON output to a file named
@@ -378,10 +386,12 @@ diagnostic_output_format_init_json_stderr (diagnostic_context *context)
 
 void
 diagnostic_output_format_init_json_file (diagnostic_context *context,
+					 bool formatted,
 					 const char *base_file_name)
 {
   diagnostic_output_format_init_json (context);
   context->set_output_format (new json_file_output_format (*context,
+							   formatted,
 							   base_file_name));
 }
 
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 941fd0f5f74..05b2c6df2e2 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -157,7 +157,8 @@ private:
 class sarif_builder
 {
 public:
-  sarif_builder (diagnostic_context *context);
+  sarif_builder (diagnostic_context *context,
+		 bool formatted);
 
   void end_diagnostic (diagnostic_context *context,
 		       const diagnostic_info &diagnostic,
@@ -250,6 +251,8 @@ private:
   hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
 
   int m_tabstop;
+
+  bool m_formatted;
 };
 
 /* class sarif_object : public json::object.  */
@@ -401,7 +404,8 @@ sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
 
 /* sarif_builder's ctor.  */
 
-sarif_builder::sarif_builder (diagnostic_context *context)
+sarif_builder::sarif_builder (diagnostic_context *context,
+			      bool formatted)
 : m_context (context),
   m_invocation_obj (new sarif_invocation ()),
   m_results_array (new json::array ()),
@@ -409,7 +413,8 @@ sarif_builder::sarif_builder (diagnostic_context *context)
   m_seen_any_relative_paths (false),
   m_rule_id_set (),
   m_rules_arr (new json::array ()),
-  m_tabstop (context->m_tabstop)
+  m_tabstop (context->m_tabstop),
+  m_formatted (formatted)
 {
 }
 
@@ -472,7 +477,7 @@ sarif_builder::flush_to_file (FILE *outf)
 {
   m_invocation_obj->prepare_to_flush (m_context);
   json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
-  top->dump (outf);
+  top->dump (outf, m_formatted);
   m_invocation_obj = NULL;
   m_results_array = NULL;
   fprintf (outf, "\n");
@@ -1721,9 +1726,10 @@ public:
   }
 
 protected:
-  sarif_output_format (diagnostic_context &context)
+  sarif_output_format (diagnostic_context &context,
+		       bool formatted)
   : diagnostic_output_format (context),
-    m_builder (&context)
+    m_builder (&context, formatted)
   {}
 
   sarif_builder m_builder;
@@ -1732,8 +1738,10 @@ protected:
 class sarif_stream_output_format : public sarif_output_format
 {
 public:
-  sarif_stream_output_format (diagnostic_context &context, FILE *stream)
-  : sarif_output_format (context),
+  sarif_stream_output_format (diagnostic_context &context,
+			      bool formatted,
+			      FILE *stream)
+  : sarif_output_format (context, formatted),
     m_stream (stream)
   {
   }
@@ -1749,8 +1757,9 @@ class sarif_file_output_format : public sarif_output_format
 {
 public:
   sarif_file_output_format (diagnostic_context &context,
-			   const char *base_file_name)
-  : sarif_output_format (context),
+			    bool formatted,
+			    const char *base_file_name)
+  : sarif_output_format (context, formatted),
     m_base_file_name (xstrdup (base_file_name))
   {
   }
@@ -1801,10 +1810,12 @@ diagnostic_output_format_init_sarif (diagnostic_context *context)
 /* Populate CONTEXT in preparation for SARIF output to stderr.  */
 
 void
-diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
+diagnostic_output_format_init_sarif_stderr (diagnostic_context *context,
+					    bool formatted)
 {
   diagnostic_output_format_init_sarif (context);
   context->set_output_format (new sarif_stream_output_format (*context,
+							      formatted,
 							      stderr));
 }
 
@@ -1813,10 +1824,12 @@ diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
 
 void
 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
+					  bool formatted,
 					  const char *base_file_name)
 {
   diagnostic_output_format_init_sarif (context);
   context->set_output_format (new sarif_file_output_format (*context,
+							    formatted,
 							    base_file_name));
 }
 
@@ -1824,9 +1837,11 @@ diagnostic_output_format_init_sarif_file (diagnostic_context *context,
 
 void
 diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
+					    bool formatted,
 					    FILE *stream)
 {
   diagnostic_output_format_init_sarif (context);
   context->set_output_format (new sarif_stream_output_format (*context,
+							      formatted,
 							      stream));
 }
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 5854c89a387..b5b6a760ccf 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -2429,7 +2429,8 @@ diagnostic_text_output_format::on_diagram (const diagnostic_diagram &diagram)
 void
 diagnostic_output_format_init (diagnostic_context *context,
 			       const char *base_file_name,
-			       enum diagnostics_output_format format)
+			       enum diagnostics_output_format format,
+			       bool json_formatting)
 {
   switch (format)
     {
@@ -2440,19 +2441,25 @@ diagnostic_output_format_init (diagnostic_context *context,
       break;
 
     case DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR:
-      diagnostic_output_format_init_json_stderr (context);
+      diagnostic_output_format_init_json_stderr (context,
+						 json_formatting);
       break;
 
     case DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE:
-      diagnostic_output_format_init_json_file (context, base_file_name);
+      diagnostic_output_format_init_json_file (context,
+					       json_formatting,
+					       base_file_name);
       break;
 
     case DIAGNOSTICS_OUTPUT_FORMAT_SARIF_STDERR:
-      diagnostic_output_format_init_sarif_stderr (context);
+      diagnostic_output_format_init_sarif_stderr (context,
+						  json_formatting);
       break;
 
     case DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE:
-      diagnostic_output_format_init_sarif_file (context, base_file_name);
+      diagnostic_output_format_init_sarif_file (context,
+						json_formatting,
+						base_file_name);
       break;
     }
 }
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 4fc31438b16..80e53ec92b0 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -1062,14 +1062,20 @@ extern char *build_message_string (const char *, ...) ATTRIBUTE_PRINTF_1;
 
 extern void diagnostic_output_format_init (diagnostic_context *,
 					   const char *base_file_name,
-					   enum diagnostics_output_format);
-extern void diagnostic_output_format_init_json_stderr (diagnostic_context *context);
+					   enum diagnostics_output_format,
+					   bool json_formatting);
+extern void diagnostic_output_format_init_json_stderr (diagnostic_context *context,
+						       bool formatted);
 extern void diagnostic_output_format_init_json_file (diagnostic_context *context,
+						     bool formatted,
 						     const char *base_file_name);
-extern void diagnostic_output_format_init_sarif_stderr (diagnostic_context *context);
+extern void diagnostic_output_format_init_sarif_stderr (diagnostic_context *context,
+							bool formatted);
 extern void diagnostic_output_format_init_sarif_file (diagnostic_context *context,
+						      bool formatted,
 						      const char *base_file_name);
 extern void diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
+							bool formatted,
 							FILE *stream);
 
 /* Compute the number of digits in the decimal representation of an integer.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ee5002cc75d..166db887e83 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -305,6 +305,7 @@ Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}
 -fdiagnostics-urls=@r{[}auto@r{|}never@r{|}always@r{]}
 -fdiagnostics-format=@r{[}text@r{|}sarif-stderr@r{|}sarif-file@r{|}json@r{|}json-stderr@r{|}json-file@r{]}
+-fno-diagnostics-json-formatting
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret
 -fno-diagnostics-show-labels  -fno-diagnostics-show-line-numbers
 -fno-diagnostics-show-cwe
@@ -5719,8 +5720,7 @@ where the JSON is emitted to - with the former, the JSON is emitted to stderr,
 whereas with @samp{json-file} it is written to @file{@var{source}.gcc.json}.
 
 The emitted JSON consists of a top-level JSON array containing JSON objects
-representing the diagnostics.  The JSON is emitted as one line, without
-formatting; the examples below have been formatted for clarity.
+representing the diagnostics.
 
 Diagnostics can have child diagnostics.  For example, this error and note:
 
@@ -5983,6 +5983,24 @@ Diagnostics have a boolean attribute @code{escape-source}, hinting whether
 non-ASCII bytes should be escaped when printing the pertinent lines of
 source code (@code{true} for diagnostics involving source encoding issues).
 
+@opindex fno-diagnostics-json-formatting
+@opindex fdiagnostics-json-formatting
+@item -fno-diagnostics-json-formatting
+By default, when JSON is emitted for diagnostics (via
+@option{-fdiagnostics-format=sarif-stderr},
+@option{-fdiagnostics-format=sarif-file},
+@option{-fdiagnostics-format=json},
+@option{-fdiagnostics-format=json-stderr},
+@option{-fdiagnostics-format=json-file}),
+GCC will add newlines and indentation to visually emphasize the
+hierarchical structure of the JSON.
+
+Use @option{-fno-diagnostics-json-formatting} to suppress this whitespace.
+It must be passed before the option it is to affect.
+
+This is intended for compatibility with tools that do not expect the output
+to contain newlines, such as that emitted by older GCC releases.
+
 @end table
 
 @node Warning Options
diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index 9f21ad9453e..c970c26e3db 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -4357,7 +4357,8 @@ driver_handle_option (struct gcc_options *opts,
 	  const char *basename = (opts->x_dump_base_name ? opts->x_dump_base_name
 				  : opts->x_main_input_basename);
 	  diagnostic_output_format_init (dc, basename,
-					 (enum diagnostics_output_format)value);
+					 (enum diagnostics_output_format)value,
+					 opts->x_flag_diagnostics_json_formatting);
 	  break;
 	}
 
diff --git a/gcc/gcov.cc b/gcc/gcov.cc
index 8b4748d3a1a..6d4a7bde53a 100644
--- a/gcc/gcov.cc
+++ b/gcc/gcov.cc
@@ -1600,13 +1600,13 @@ generate_results (const char *file_name)
     {
       if (flag_use_stdout)
 	{
-	  root->dump (stdout);
+	  root->dump (stdout, false);
 	  printf ("\n");
 	}
       else
 	{
 	  pretty_printer pp;
-	  root->print (&pp);
+	  root->print (&pp, false);
 	  pp_formatted_text (&pp);
 
 	  fnotice (stdout, "Creating '%s'\n",
diff --git a/gcc/json.cc b/gcc/json.cc
index d0f157f0dfe..90ddd7ab3b1 100644
--- a/gcc/json.cc
+++ b/gcc/json.cc
@@ -32,17 +32,15 @@ using namespace json;
 
 /* Dump this json::value tree to OUTF.
 
-   No formatting is done.
-
    The key/value pairs of json::objects are printed in the order
    in which the keys were originally inserted.  */
 
 void
-value::dump (FILE *outf) const
+value::dump (FILE *outf, bool formatted) const
 {
   pretty_printer pp;
   pp_buffer (&pp)->stream = outf;
-  print (&pp);
+  print (&pp, formatted);
   pp_flush (&pp);
 }
 
@@ -63,9 +61,11 @@ object::~object ()
 /* Implementation of json::value::print for json::object.  */
 
 void
-object::print (pretty_printer *pp) const
+object::print (pretty_printer *pp, bool formatted) const
 {
   pp_character (pp, '{');
+  if (formatted)
+    pp_indentation (pp) += 1;
 
   /* Iterate in the order that the keys were inserted.  */
   unsigned i;
@@ -73,15 +73,31 @@ object::print (pretty_printer *pp) const
   FOR_EACH_VEC_ELT (m_keys, i, key)
     {
       if (i > 0)
-	pp_string (pp, ", ");
+	{
+	  pp_string (pp, ",");
+	  if (formatted)
+	    {
+	      pp_newline (pp);
+	      pp_indent (pp);
+	    }
+	  else
+	    pp_space (pp);
+	}
       map_t &mut_map = const_cast<map_t &> (m_map);
       value *value = *mut_map.get (key);
       pp_doublequote (pp);
       pp_string (pp, key); // FIXME: escaping?
       pp_doublequote (pp);
       pp_string (pp, ": ");
-      value->print (pp);
+      const int indent = strlen (key) + 4;
+      if (formatted)
+	pp_indentation (pp) += indent;
+      value->print (pp, formatted);
+      if (formatted)
+	pp_indentation (pp) -= indent;
     }
+  if (formatted)
+    pp_indentation (pp) -= 1;
   pp_character (pp, '}');
 }
 
@@ -180,17 +196,30 @@ array::~array ()
 /* Implementation of json::value::print for json::array.  */
 
 void
-array::print (pretty_printer *pp) const
+array::print (pretty_printer *pp, bool formatted) const
 {
   pp_character (pp, '[');
+  if (formatted)
+    pp_indentation (pp) += 1;
   unsigned i;
   value *v;
   FOR_EACH_VEC_ELT (m_elements, i, v)
     {
       if (i)
-	pp_string (pp, ", ");
-      v->print (pp);
+	{
+	  pp_string (pp, ",");
+	  if (formatted)
+	    {
+	      pp_newline (pp);
+	      pp_indent (pp);
+	    }
+	  else
+	    pp_space (pp);
+	}
+      v->print (pp, formatted);
     }
+  if (formatted)
+    pp_indentation (pp) -= 1;
   pp_character (pp, ']');
 }
 
@@ -208,7 +237,8 @@ array::append (value *v)
 /* Implementation of json::value::print for json::float_number.  */
 
 void
-float_number::print (pretty_printer *pp) const
+float_number::print (pretty_printer *pp,
+		     bool formatted ATTRIBUTE_UNUSED) const
 {
   char tmp[1024];
   snprintf (tmp, sizeof (tmp), "%g", m_value);
@@ -220,7 +250,8 @@ float_number::print (pretty_printer *pp) const
 /* Implementation of json::value::print for json::integer_number.  */
 
 void
-integer_number::print (pretty_printer *pp) const
+integer_number::print (pretty_printer *pp,
+		       bool formatted ATTRIBUTE_UNUSED) const
 {
   char tmp[1024];
   snprintf (tmp, sizeof (tmp), "%ld", m_value);
@@ -250,7 +281,8 @@ string::string (const char *utf8, size_t len)
 /* Implementation of json::value::print for json::string.  */
 
 void
-string::print (pretty_printer *pp) const
+string::print (pretty_printer *pp,
+	       bool formatted ATTRIBUTE_UNUSED) const
 {
   pp_character (pp, '"');
   for (size_t i = 0; i != m_len; ++i)
@@ -294,7 +326,8 @@ string::print (pretty_printer *pp) const
 /* Implementation of json::value::print for json::literal.  */
 
 void
-literal::print (pretty_printer *pp) const
+literal::print (pretty_printer *pp,
+		bool formatted ATTRIBUTE_UNUSED) const
 {
   switch (m_kind)
     {
@@ -322,15 +355,18 @@ namespace selftest {
 /* Verify that JV->print () prints EXPECTED_JSON.  */
 
 static void
-assert_print_eq (const location &loc, const json::value &jv, const char *expected_json)
+assert_print_eq (const location &loc,
+		 const json::value &jv,
+		 bool formatted,
+		 const char *expected_json)
 {
   pretty_printer pp;
-  jv.print (&pp);
+  jv.print (&pp, formatted);
   ASSERT_STREQ_AT (loc, expected_json, pp_formatted_text (&pp));
 }
 
-#define ASSERT_PRINT_EQ(JV, EXPECTED_JSON) \
-  assert_print_eq (SELFTEST_LOCATION, JV, EXPECTED_JSON)
+#define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON)	\
+  assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
 
 /* Verify that object::get works as expected.  */
 
@@ -354,7 +390,11 @@ test_writing_objects ()
   obj.set_string ("baz", "quux");
   /* This test relies on json::object writing out key/value pairs
      in key-insertion order.  */
-  ASSERT_PRINT_EQ (obj, "{\"foo\": \"bar\", \"baz\": \"quux\"}");
+  ASSERT_PRINT_EQ (obj, true,
+		   "{\"foo\": \"bar\",\n"
+		   " \"baz\": \"quux\"}");
+  ASSERT_PRINT_EQ (obj, false,
+		   "{\"foo\": \"bar\", \"baz\": \"quux\"}");
 }
 
 /* Verify that JSON arrays are written correctly.  */
@@ -363,13 +403,17 @@ static void
 test_writing_arrays ()
 {
   array arr;
-  ASSERT_PRINT_EQ (arr, "[]");
+  ASSERT_PRINT_EQ (arr, true, "[]");
 
   arr.append (new json::string ("foo"));
-  ASSERT_PRINT_EQ (arr, "[\"foo\"]");
+  ASSERT_PRINT_EQ (arr, true, "[\"foo\"]");
 
   arr.append (new json::string ("bar"));
-  ASSERT_PRINT_EQ (arr, "[\"foo\", \"bar\"]");
+  ASSERT_PRINT_EQ (arr, true,
+		   "[\"foo\",\n"
+		   " \"bar\"]");
+  ASSERT_PRINT_EQ (arr, false,
+		   "[\"foo\", \"bar\"]");
 }
 
 /* Verify that JSON numbers are written correctly.  */
@@ -377,20 +421,20 @@ test_writing_arrays ()
 static void
 test_writing_float_numbers ()
 {
-  ASSERT_PRINT_EQ (float_number (0), "0");
-  ASSERT_PRINT_EQ (float_number (42), "42");
-  ASSERT_PRINT_EQ (float_number (-100), "-100");
-  ASSERT_PRINT_EQ (float_number (123456789), "1.23457e+08");
+  ASSERT_PRINT_EQ (float_number (0), true, "0");
+  ASSERT_PRINT_EQ (float_number (42), true, "42");
+  ASSERT_PRINT_EQ (float_number (-100), true, "-100");
+  ASSERT_PRINT_EQ (float_number (123456789), true, "1.23457e+08");
 }
 
 static void
 test_writing_integer_numbers ()
 {
-  ASSERT_PRINT_EQ (integer_number (0), "0");
-  ASSERT_PRINT_EQ (integer_number (42), "42");
-  ASSERT_PRINT_EQ (integer_number (-100), "-100");
-  ASSERT_PRINT_EQ (integer_number (123456789), "123456789");
-  ASSERT_PRINT_EQ (integer_number (-123456789), "-123456789");
+  ASSERT_PRINT_EQ (integer_number (0), true, "0");
+  ASSERT_PRINT_EQ (integer_number (42), true, "42");
+  ASSERT_PRINT_EQ (integer_number (-100), true, "-100");
+  ASSERT_PRINT_EQ (integer_number (123456789), true, "123456789");
+  ASSERT_PRINT_EQ (integer_number (-123456789), true, "-123456789");
 }
 
 /* Verify that JSON strings are written correctly.  */
@@ -399,16 +443,16 @@ static void
 test_writing_strings ()
 {
   string foo ("foo");
-  ASSERT_PRINT_EQ (foo, "\"foo\"");
+  ASSERT_PRINT_EQ (foo, true, "\"foo\"");
 
   string contains_quotes ("before \"quoted\" after");
-  ASSERT_PRINT_EQ (contains_quotes, "\"before \\\"quoted\\\" after\"");
+  ASSERT_PRINT_EQ (contains_quotes, true, "\"before \\\"quoted\\\" after\"");
 
   const char data[] = {'a', 'b', 'c', 'd', '\0', 'e', 'f'};
   string not_terminated (data, 3);
-  ASSERT_PRINT_EQ (not_terminated, "\"abc\"");
+  ASSERT_PRINT_EQ (not_terminated, true, "\"abc\"");
   string embedded_null (data, sizeof data);
-  ASSERT_PRINT_EQ (embedded_null, "\"abcd\\0ef\"");
+  ASSERT_PRINT_EQ (embedded_null, true, "\"abcd\\0ef\"");
 }
 
 /* Verify that JSON literals are written correctly.  */
@@ -416,12 +460,50 @@ test_writing_strings ()
 static void
 test_writing_literals ()
 {
-  ASSERT_PRINT_EQ (literal (JSON_TRUE), "true");
-  ASSERT_PRINT_EQ (literal (JSON_FALSE), "false");
-  ASSERT_PRINT_EQ (literal (JSON_NULL), "null");
+  ASSERT_PRINT_EQ (literal (JSON_TRUE), true, "true");
+  ASSERT_PRINT_EQ (literal (JSON_FALSE), true, "false");
+  ASSERT_PRINT_EQ (literal (JSON_NULL), true, "null");
 
-  ASSERT_PRINT_EQ (literal (true), "true");
-  ASSERT_PRINT_EQ (literal (false), "false");
+  ASSERT_PRINT_EQ (literal (true), true, "true");
+  ASSERT_PRINT_EQ (literal (false), true, "false");
+}
+
+/* Verify that nested values are formatted correctly when written.  */
+
+static void
+test_formatting ()
+{
+  object obj;
+  object *child = new object;
+  object *grandchild = new object;
+
+  obj.set_string ("str", "bar");
+  obj.set ("child", child);
+  obj.set_integer ("int", 42);
+
+  child->set ("grandchild", grandchild);
+  child->set_integer ("int", 1776);
+
+  array *arr = new array;
+  for (int i = 0; i < 3; i++)
+    arr->append (new integer_number (i));
+  grandchild->set ("arr", arr);
+  grandchild->set_integer ("int", 1066);
+
+  /* This test relies on json::object writing out key/value pairs
+     in key-insertion order.  */
+  ASSERT_PRINT_EQ (obj, true,
+		   ("{\"str\": \"bar\",\n"
+		    " \"child\": {\"grandchild\": {\"arr\": [0,\n"
+		    "                                  1,\n"
+		    "                                  2],\n"
+		    "                          \"int\": 1066},\n"
+		    "           \"int\": 1776},\n"
+		    " \"int\": 42}"));
+  ASSERT_PRINT_EQ (obj, false,
+		   ("{\"str\": \"bar\", \"child\": {\"grandchild\":"
+		    " {\"arr\": [0, 1, 2], \"int\": 1066},"
+		    " \"int\": 1776}, \"int\": 42}"));
 }
 
 /* Run all of the selftests within this file.  */
@@ -436,6 +518,7 @@ json_cc_tests ()
   test_writing_integer_numbers ();
   test_writing_strings ();
   test_writing_literals ();
+  test_formatting ();
 }
 
 } // namespace selftest
diff --git a/gcc/json.h b/gcc/json.h
index 6fadd119ba5..862e5676a63 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -80,9 +80,9 @@ class value
  public:
   virtual ~value () {}
   virtual enum kind get_kind () const = 0;
-  virtual void print (pretty_printer *pp) const = 0;
+  virtual void print (pretty_printer *pp, bool formatted) const = 0;
 
-  void dump (FILE *) const;
+  void dump (FILE *, bool formatted) const;
 };
 
 /* Subclass of value for objects: a collection of key/value pairs
@@ -97,7 +97,7 @@ class object : public value
   ~object ();
 
   enum kind get_kind () const final override { return JSON_OBJECT; }
-  void print (pretty_printer *pp) const final override;
+  void print (pretty_printer *pp, bool formatted) const final override;
 
   void set (const char *key, value *v);
   value *get (const char *key) const;
@@ -126,7 +126,7 @@ class array : public value
   ~array ();
 
   enum kind get_kind () const final override { return JSON_ARRAY; }
-  void print (pretty_printer *pp) const final override;
+  void print (pretty_printer *pp, bool formatted) const final override;
 
   void append (value *v);
 
@@ -142,7 +142,7 @@ class float_number : public value
   float_number (double value) : m_value (value) {}
 
   enum kind get_kind () const final override { return JSON_FLOAT; }
-  void print (pretty_printer *pp) const final override;
+  void print (pretty_printer *pp, bool formatted) const final override;
 
   double get () const { return m_value; }
 
@@ -158,7 +158,7 @@ class integer_number : public value
   integer_number (long value) : m_value (value) {}
 
   enum kind get_kind () const final override { return JSON_INTEGER; }
-  void print (pretty_printer *pp) const final override;
+  void print (pretty_printer *pp, bool formatted) const final override;
 
   long get () const { return m_value; }
 
@@ -177,7 +177,7 @@ class string : public value
   ~string () { free (m_utf8); }
 
   enum kind get_kind () const final override { return JSON_STRING; }
-  void print (pretty_printer *pp) const final override;
+  void print (pretty_printer *pp, bool formatted) const final override;
 
   const char *get_string () const { return m_utf8; }
   size_t get_length () const { return m_len; }
@@ -199,7 +199,7 @@ class literal : public value
   literal (bool value): m_kind (value ? JSON_TRUE : JSON_FALSE) {}
 
   enum kind get_kind () const final override { return m_kind; }
-  void print (pretty_printer *pp) const final override;
+  void print (pretty_printer *pp, bool formatted) const final override;
 
  private:
   enum kind m_kind;
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
index 11cad42a433..b181d6fb15d 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -103,7 +103,7 @@ void
 optrecord_json_writer::write () const
 {
   pretty_printer pp;
-  m_root_tuple->print (&pp);
+  m_root_tuple->print (&pp, false);
 
   bool emitted_error = false;
   char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
@@ -466,7 +466,7 @@ test_building_json_from_dump_calls ()
 
   /* Verify that the json is sane.  */
   pretty_printer pp;
-  json_obj->print (&pp);
+  json_obj->print (&pp, false);
   const char *json_str = pp_formatted_text (&pp);
   ASSERT_STR_CONTAINS (json_str, "impl_location");
   ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
diff --git a/gcc/opts.cc b/gcc/opts.cc
index 5d5efaf1b9e..7a3830caaa3 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -2949,7 +2949,8 @@ common_handle_option (struct gcc_options *opts,
 	  const char *basename = (opts->x_dump_base_name ? opts->x_dump_base_name
 				  : opts->x_main_input_basename);
 	  diagnostic_output_format_init (dc, basename,
-					 (enum diagnostics_output_format)value);
+					 (enum diagnostics_output_format)value,
+					 opts->x_flag_diagnostics_json_formatting);
 	  break;
 	}
 
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-1.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-1.c
index 6bab30e3e6c..c95218c3cfe 100644
--- a/gcc/testsuite/c-c++-common/diagnostic-format-json-1.c
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-1.c
@@ -3,28 +3,20 @@
 
 #error message
 
-/* Use dg-regexp to consume the JSON output starting with
-   the innermost values, and working outwards.  */
-
-/* { dg-regexp "\"kind\": \"error\"" } */
-/* { dg-regexp "\"column-origin\": 1" } */
-/* { dg-regexp "\"escape-source\": false" } */
-/* { dg-regexp "\"message\": \"#error message\"" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.c\"" } */
-/* { dg-regexp "\"line\": 4" } */
-/* { dg-regexp "\"column\": 2" } */
-/* { dg-regexp "\"display-column\": 2" } */
-/* { dg-regexp "\"byte-column\": 2" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.c\"" } */
-/* { dg-regexp "\"line\": 4" } */
-/* { dg-regexp "\"column\": 6" } */
-/* { dg-regexp "\"display-column\": 6" } */
-/* { dg-regexp "\"byte-column\": 6" } */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
-/* { dg-regexp "\[\[\{\}, \]*\]" } */
+/* { dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "#error message",
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 4,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 4,
+                            "display-column": 6,
+                            "byte-column": 6,
+                            "column": 6}}],
+  "escape-source": false}]
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c
index 3c12103c9f8..a8828b7b2df 100644
--- a/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-2.c
@@ -3,30 +3,24 @@
 
 #warning message
 
-/* Use dg-regexp to consume the JSON output starting with
-   the innermost values, and working outwards.  */
-
-/* { dg-regexp "\"kind\": \"warning\"" } */
-/* { dg-regexp "\"column-origin\": 1" } */
-/* { dg-regexp "\"escape-source\": false" } */
-/* { dg-regexp "\"message\": \"#warning message\"" } */
-/* { dg-regexp "\"option\": \"-Wcpp\"" } */
-/* { dg-regexp "\"option_url\": \"https:\[^\n\r\"\]*#index-Wcpp\"" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.c\"" } */
-/* { dg-regexp "\"line\": 4" } */
-/* { dg-regexp "\"column\": 2" } */
-/* { dg-regexp "\"display-column\": 2" } */
-/* { dg-regexp "\"byte-column\": 2" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.c\"" } */
-/* { dg-regexp "\"line\": 4" } */
-/* { dg-regexp "\"column\": 8" } */
-/* { dg-regexp "\"display-column\": 8" } */
-/* { dg-regexp "\"byte-column\": 8" } */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
-/* { dg-regexp "\[\[\{\}, \]*\]" } */
+/* { dg-begin-multiline-output "" }
+[{"kind": "warning",
+  "message": "#warning message",
+  "option": "-Wcpp",
+   { dg-end-multiline-output "" } */
+/* { dg-regexp "  \"option_url\": \"https:\[^\n\r\"\]*#index-Wcpp\",\n" } */
+/* { dg-begin-multiline-output "" }
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 4,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 4,
+                            "display-column": 8,
+                            "byte-column": 8,
+                            "column": 8}}],
+  "escape-source": false}]
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c
index 11d74624ff1..178bbf94b5b 100644
--- a/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-3.c
@@ -3,30 +3,24 @@
 
 #warning message
 
-/* Use dg-regexp to consume the JSON output starting with
-   the innermost values, and working outwards.  */
-
-/* { dg-regexp "\"kind\": \"error\"" } */
-/* { dg-regexp "\"column-origin\": 1" } */
-/* { dg-regexp "\"escape-source\": false" } */
-/* { dg-regexp "\"message\": \"#warning message\"" } */
-/* { dg-regexp "\"option\": \"-Werror=cpp\"" } */
-/* { dg-regexp "\"option_url\": \"https:\[^\n\r\"\]*#index-Wcpp\"" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.c\"" } */
-/* { dg-regexp "\"line\": 4" } */
-/* { dg-regexp "\"column\": 2" } */
-/* { dg-regexp "\"display-column\": 2" } */
-/* { dg-regexp "\"byte-column\": 2" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.c\"" } */
-/* { dg-regexp "\"line\": 4" } */
-/* { dg-regexp "\"column\": 8" } */
-/* { dg-regexp "\"display-column\": 8" } */
-/* { dg-regexp "\"byte-column\": 8" } */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
-/* { dg-regexp "\[\[\{\}, \]*\]" } */
+/* { dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "#warning message",
+  "option": "-Werror=cpp",
+   { dg-end-multiline-output "" } */
+/* { dg-regexp "  \"option_url\": \"https:\[^\n\r\"\]*#index-Wcpp\",\n" } */
+/* { dg-begin-multiline-output "" }
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 4,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 4,
+                            "display-column": 8,
+                            "byte-column": 8,
+                            "column": 8}}],
+  "escape-source": false}]
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c
index cec1cf924b4..899a03f0e5e 100644
--- a/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-4.c
@@ -9,63 +9,36 @@ int test (void)
   return 5;
 }
 
-/* Use dg-regexp to consume the JSON output starting with
-   the innermost values, and working outwards.  */
-
-/* Verify nested diagnostics.  */
-
-/* The nested diagnostic.  */
-
-/* { dg-regexp "\"kind\": \"note\"" } */
-/* { dg-regexp "\"message\": \"...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'\"" } */
-/* { dg-regexp "\"escape-source\": false" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
-/* { dg-regexp "\"line\": 8" } */
-/* { dg-regexp "\"column\": 5" } */
-/* { dg-regexp "\"display-column\": 5" } */
-/* { dg-regexp "\"byte-column\": 5" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
-/* { dg-regexp "\"line\": 8" } */
-/* { dg-regexp "\"column\": 10" } */
-/* { dg-regexp "\"display-column\": 10" } */
-/* { dg-regexp "\"byte-column\": 10" } */
-
-/* The outer diagnostic.  */
-
-/* { dg-regexp "\"kind\": \"warning\"" } */
-/* { dg-regexp "\"column-origin\": 1" } */
-/* { dg-regexp "\"message\": \"this 'if' clause does not guard...\"" } */
-/* { dg-regexp "\"escape-source\": false" } */
-/* { dg-regexp "\"option\": \"-Wmisleading-indentation\"" } */
-/* { dg-regexp "\"option_url\": \"https:\[^\n\r\"\]*#index-Wmisleading-indentation\"" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
-/* { dg-regexp "\"line\": 6" } */
-/* { dg-regexp "\"column\": 3" } */
-/* { dg-regexp "\"display-column\": 3" } */
-/* { dg-regexp "\"byte-column\": 3" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-4.c\"" } */
-/* { dg-regexp "\"line\": 6" } */
-/* { dg-regexp "\"column\": 4" } */
-/* { dg-regexp "\"display-column\": 4" } */
-/* { dg-regexp "\"byte-column\": 4" } */
-
-/* More from the nested diagnostic (we can't guarantee what order the
-   "file" keys are consumed).  */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-
-/* More from the outer diagnostic.  */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-
-/* { dg-regexp "\"children\": \[\[\{\}, \]*\]" } */
-/* { dg-regexp "\[\[\{\}, \]*\]" } */
-
+/* { dg-begin-multiline-output "" }
+[{"kind": "warning",
+  "message": "this 'if' clause does not guard...",
+  "option": "-Wmisleading-indentation",
+   { dg-end-multiline-output "" } */
+/* { dg-regexp "  \"option_url\": \"https:\[^\n\r\"\]*#index-Wmisleading-indentation\",\n" } */
+/* { dg-begin-multiline-output "" }
+  "children": [{"kind": "note",
+                "message": "...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'",
+                "locations": [{"caret": {"file":
+                                         "line": 8,
+                                         "display-column": 5,
+                                         "byte-column": 5,
+                                         "column": 5},
+                               "finish": {"file":
+                                          "line": 8,
+                                          "display-column": 10,
+                                          "byte-column": 10,
+                                          "column": 10}}],
+                "escape-source": false}],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 6,
+                           "display-column": 3,
+                           "byte-column": 3,
+                           "column": 3},
+                 "finish": {"file":
+                            "line": 6,
+                            "display-column": 4,
+                            "byte-column": 4,
+                            "column": 4}}],
+  "escape-source": false}]
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-5.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-5.c
index 86f8c5fb374..ed3139c7f1b 100644
--- a/gcc/testsuite/c-c++-common/diagnostic-format-json-5.c
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-5.c
@@ -8,61 +8,31 @@ int test (struct s *ptr)
   return ptr->colour;
 }
 
-/* Verify fix-it hints.
-
-   Use dg-regexp to consume the JSON output from start to
-   finish, relying on the ordering of the keys.
-   The following uses indentation to visualize the structure
-   of the JSON (although the actual output is all on one line).
-
-  { dg-regexp {\[} }
-    { dg-regexp {\{} }
-     { dg-regexp {"kind": "error"} }
-     { dg-regexp {, "message": "'struct s' has no member named 'colour'; did you mean 'color'\?"} }
-     { dg-regexp {, "children": \[\]} }
-     { dg-regexp {, "column-origin": 1} }
-     { dg-regexp {, "locations": } }
-       { dg-regexp {\[} }
-         { dg-regexp {\{} }
-           { dg-regexp {"caret": } }
-             { dg-regexp {\{} }
-               { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
-               { dg-regexp {, "line": 8} }
-               { dg-regexp {, "display-column": 15} }
-               { dg-regexp {, "byte-column": 15} }
-               { dg-regexp {, "column": 15} }
-             { dg-regexp {\}} }
-           { dg-regexp {, "finish": } }
-             { dg-regexp {\{} }
-               { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
-               { dg-regexp {, "line": 8} }
-               { dg-regexp {, "display-column": 20} }
-               { dg-regexp {, "byte-column": 20} }
-               { dg-regexp {, "column": 20} }
-             { dg-regexp {\}} }
-           { dg-regexp {\}} }
-         { dg-regexp {\]} }
-       { dg-regexp {, "fixits": } }
-       { dg-regexp {\[} }
-         { dg-regexp {\{} }
-           { dg-regexp {"start": } }
-             { dg-regexp {\{} }
-               { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
-               { dg-regexp {, "line": 8} }
-               { dg-regexp {, "display-column": 15} }
-               { dg-regexp {, "byte-column": 15} }
-               { dg-regexp {, "column": 15} }
-             { dg-regexp {\}} }
-           { dg-regexp {, "next": } }
-             { dg-regexp {\{} }
-               { dg-regexp {"file": "[^\n\r"]*diagnostic-format-json-5.c"} }
-               { dg-regexp {, "line": 8} }
-               { dg-regexp {, "display-column": 21} }
-               { dg-regexp {, "byte-column": 21} }
-               { dg-regexp {, "column": 21} }
-             { dg-regexp {\}} }
-           { dg-regexp {, "string": "color"} }
-         { dg-regexp {\}} }
-       { dg-regexp {\]} }
-     { dg-regexp {, "escape-source": false\}} }
-   { dg-regexp {\]} }  */
+/* { dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "'struct s' has no member named 'colour'; did you mean 'color'?",
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 8,
+                           "display-column": 15,
+                           "byte-column": 15,
+                           "column": 15},
+                 "finish": {"file":
+                            "line": 8,
+                            "display-column": 20,
+                            "byte-column": 20,
+                            "column": 20}}],
+  "fixits": [{"start": {"file":
+                        "line": 8,
+                        "display-column": 15,
+                        "byte-column": 15,
+                        "column": 15},
+              "next": {"file":
+                       "line": 8,
+                       "display-column": 21,
+                       "byte-column": 21,
+                       "column": 21},
+              "string": "color"}],
+  "escape-source": false}]
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c b/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c
index bcfa92110f5..e798c6b21e1 100644
--- a/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-json-stderr-1.c
@@ -5,28 +5,20 @@
 
 #error message
 
-/* Use dg-regexp to consume the JSON output starting with
-   the innermost values, and working outwards.  */
-
-/* { dg-regexp "\"kind\": \"error\"" } */
-/* { dg-regexp "\"column-origin\": 1" } */
-/* { dg-regexp "\"escape-source\": false" } */
-/* { dg-regexp "\"message\": \"#error message\"" } */
-
-/* { dg-regexp "\"caret\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-stderr-1.c\"" } */
-/* { dg-regexp "\"line\": 6" } */
-/* { dg-regexp "\"column\": 2" } */
-/* { dg-regexp "\"display-column\": 2" } */
-/* { dg-regexp "\"byte-column\": 2" } */
-
-/* { dg-regexp "\"finish\": \{" } */
-/* { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-stderr-1.c\"" } */
-/* { dg-regexp "\"line\": 6" } */
-/* { dg-regexp "\"column\": 6" } */
-/* { dg-regexp "\"display-column\": 6" } */
-/* { dg-regexp "\"byte-column\": 6" } */
-
-/* { dg-regexp "\"locations\": \[\[\{\}, \]*\]" } */
-/* { dg-regexp "\"children\": \[\[\]\[\]\]" } */
-/* { dg-regexp "\[\[\{\}, \]*\]" } */
+/* { dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "#error message",
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 6,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 6,
+                            "display-column": 6,
+                            "byte-column": 6,
+                            "column": 6}}],
+  "escape-source": false}]
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/g++.dg/pr90462.C b/gcc/testsuite/g++.dg/pr90462.C
index 2585ba0dcdb..b35e41921a6 100644
--- a/gcc/testsuite/g++.dg/pr90462.C
+++ b/gcc/testsuite/g++.dg/pr90462.C
@@ -1,4 +1,4 @@
-/* { dg-options "-Wdeprecated-copy -fdiagnostics-format=json" } */
+/* { dg-options "-Wdeprecated-copy -fno-diagnostics-json-formatting -fdiagnostics-format=json" } */
 
 template <class> class b;
 struct B {
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c
index 3d798e687e6..19ac89f2b67 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-additional-options "-fdiagnostics-format=sarif-file" } */
+/* { dg-additional-options " -fno-diagnostics-json-formatting -fdiagnostics-format=sarif-file" } */
 
 #include <stdlib.h>
 
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-3.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-3.c
index 6971d7cb38b..a315d208cab 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-3.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-3.c
@@ -32,7 +32,44 @@ make_a_list_of_random_ints_badly(PyObject *self,
   return list;
 }
 
-/* FIXME: test the events within a path.  */
-/* { dg-regexp "\"kind\": \"error\"" } */
-/* { dg-regexp "\"path\": " } */
-/* { dg-regexp ".*" } */
+/* { dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter",
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file": "
+                           "line": 29,
+                           "display-column": 5,
+                           "byte-column": 5,
+                           "column": 5},
+                 "finish": {"file": "
+                            "line": 29,
+                            "display-column": 29,
+                            "byte-column": 29,
+                            "column": 29}}],
+  "path": [{"location": {"file": "
+                         "line": 25,
+                         "display-column": 10,
+                         "byte-column": 10,
+                         "column": 10},
+            "description": "when 'PyList_New' fails, returning NULL",
+            "function": "make_a_list_of_random_ints_badly",
+            "depth": 0},
+           {"location": {"file": "
+                         "line": 27,
+                         "display-column": 17,
+                         "byte-column": 17,
+                         "column": 17},
+            "description": "when 'i < count'",
+            "function": "make_a_list_of_random_ints_badly",
+            "depth": 0},
+           {"location": {"file": "
+                         "line": 29,
+                         "display-column": 5,
+                         "byte-column": 5,
+                         "column": 5},
+            "description": "when calling 'PyList_Append', passing NULL from (1) as argument 1",
+            "function": "make_a_list_of_random_ints_badly",
+            "depth": 0}],
+  "escape-source": false}]
+{ dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gfortran.dg/diagnostic-format-json-1.F90 b/gcc/testsuite/gfortran.dg/diagnostic-format-json-1.F90
index 2993f7c852b..b8cd61cff23 100644
--- a/gcc/testsuite/gfortran.dg/diagnostic-format-json-1.F90
+++ b/gcc/testsuite/gfortran.dg/diagnostic-format-json-1.F90
@@ -3,29 +3,22 @@
 
 #error message
 
-! Use dg-regexp to consume the JSON output starting with
-! the innermost values, and working outwards.
-! We can't rely on any ordering of the keys.
-
-! { dg-regexp "\"kind\": \"error\"" }
-! { dg-regexp "\"column-origin\": 1" }
-! { dg-regexp "\"escape-source\": false" }
-! { dg-regexp "\"message\": \"#error message\"" }
-
-! { dg-regexp "\"caret\": \{" }
-! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.F90\"" }
-! { dg-regexp "\"line\": 4" }
-! { dg-regexp "\"column\": 2" }
-! { dg-regexp "\"display-column\": 2" }
-! { dg-regexp "\"byte-column\": 2" }
-
-! { dg-regexp "\"finish\": \{" }
-! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-1.F90\"" }
-! { dg-regexp "\"line\": 4" }
-! { dg-regexp "\"column\": 6" }
-! { dg-regexp "\"display-column\": 6" }
-! { dg-regexp "\"byte-column\": 6" }
-
-! { dg-regexp "\"locations\": \[\[\{\}, \]*\]" }
-! { dg-regexp "\"children\": \[\[\]\[\]\]" }
-! { dg-regexp "\[\[\{\}, \]*\]" }
+#if 0
+{ dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "#error message",
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 4,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 4,
+                            "display-column": 6,
+                            "byte-column": 6,
+                            "column": 6}}],
+  "escape-source": false}]
+{ dg-end-multiline-output "" }
+#endif  
diff --git a/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90 b/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90
index 1681462fa08..9ff1ef59b34 100644
--- a/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90
+++ b/gcc/testsuite/gfortran.dg/diagnostic-format-json-2.F90
@@ -3,31 +3,24 @@
 
 #warning message
 
-! Use dg-regexp to consume the JSON output starting with
-! the innermost values, and working outwards.
-! We can't rely on any ordering of the keys. 
-
-! { dg-regexp "\"kind\": \"warning\"" }
-! { dg-regexp "\"column-origin\": 1" }
-! { dg-regexp "\"escape-source\": false" }
-! { dg-regexp "\"message\": \"#warning message\"" }
-! { dg-regexp "\"option\": \"-Wcpp\"" }
-! { dg-regexp "\"option_url\": \"\[^\n\r\"\]*#index-Wcpp\"" }
-
-! { dg-regexp "\"caret\": \{" }
-! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.F90\"" }
-! { dg-regexp "\"line\": 4" }
-! { dg-regexp "\"column\": 2" }
-! { dg-regexp "\"display-column\": 2" }
-! { dg-regexp "\"byte-column\": 2" }
-
-! { dg-regexp "\"finish\": \{" }
-! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-2.F90\"" }
-! { dg-regexp "\"line\": 4" }
-! { dg-regexp "\"column\": 8" }
-! { dg-regexp "\"display-column\": 8" }
-! { dg-regexp "\"byte-column\": 8" }
-
-! { dg-regexp "\"locations\": \[\[\{\}, \]*\]" }
-! { dg-regexp "\"children\": \[\[\]\[\]\]" }
-! { dg-regexp "\[\[\{\}, \]*\]" }
+#if 0
+{ dg-begin-multiline-output "" }
+[{"kind": "warning",
+  "message": "#warning message",
+  "option": "-Wcpp",
+  "option_url":
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 4,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 4,
+                            "display-column": 8,
+                            "byte-column": 8,
+                            "column": 8}}],
+  "escape-source": false}]
+{ dg-end-multiline-output "" }
+#endif  
diff --git a/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90 b/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90
index f0a67de76b0..750e186c8ac 100644
--- a/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90
+++ b/gcc/testsuite/gfortran.dg/diagnostic-format-json-3.F90
@@ -3,31 +3,24 @@
 
 #warning message
 
-! Use dg-regexp to consume the JSON output starting with
-! the innermost values, and working outwards.
-! We can't rely on any ordering of the keys.
-
-! { dg-regexp "\"kind\": \"error\"" }
-! { dg-regexp "\"column-origin\": 1" }
-! { dg-regexp "\"escape-source\": false" }
-! { dg-regexp "\"message\": \"#warning message\"" }
-! { dg-regexp "\"option\": \"-Werror=cpp\"" }
-! { dg-regexp "\"option_url\": \"\[^\n\r\"\]*#index-Wcpp\"" }
-
-! { dg-regexp "\"caret\": \{" }
-! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.F90\"" }
-! { dg-regexp "\"line\": 4" }
-! { dg-regexp "\"column\": 2" }
-! { dg-regexp "\"display-column\": 2" }
-! { dg-regexp "\"byte-column\": 2" }
-
-! { dg-regexp "\"finish\": \{" }
-! { dg-regexp "\"file\": \"\[^\n\r\"\]*diagnostic-format-json-3.F90\"" }
-! { dg-regexp "\"line\": 4" }
-! { dg-regexp "\"column\": 8" }
-! { dg-regexp "\"display-column\": 8" }
-! { dg-regexp "\"byte-column\": 8" }
-
-! { dg-regexp "\"locations\": \[\[\{\}, \]*\]" }
-! { dg-regexp "\"children\": \[\[\]\[\]\]" }
-! { dg-regexp "\[\[\{\}, \]*\]" }
+#if 0
+{ dg-begin-multiline-output "" }
+[{"kind": "error",
+  "message": "#warning message",
+  "option": "-Werror=cpp",
+  "option_url":
+  "children": [],
+  "column-origin": 1,
+  "locations": [{"caret": {"file":
+                           "line": 4,
+                           "display-column": 2,
+                           "byte-column": 2,
+                           "column": 2},
+                 "finish": {"file":
+                            "line": 4,
+                            "display-column": 8,
+                            "byte-column": 8,
+                            "column": 8}}],
+  "escape-source": false}]
+{ dg-end-multiline-output "" }
+#endif  
