diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 60addf2871f3439fc0cafa2e3cb64a960c627cb8..feda0e00357407a7cdd7ac7a8239771c3d868955 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -84,6 +84,10 @@ static void array_dim_to_json(StringInfo result, int dim, int ndims,int * dims,
 							  Oid typoutputfunc, bool use_line_feeds);
 static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds);
 
+/* fake type category for JSON so we can distinguish it in datum_to_json */
+#define TYPCATEGORY_JSON 'j'
+/* letters appearing in numeric output that aren't valid in a JSON number */
+#define NON_NUMERIC_LETTER "NnAnIiFfTtYy"
 /*
  * Input.
  */
@@ -707,10 +711,20 @@ datum_to_json(Datum val, StringInfo result, TYPCATEGORY tcategory,
 		case TYPCATEGORY_NUMERIC:
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			/*
-			 * Don't call escape_json here. Numeric output should
-			 * be a valid JSON number and JSON numbers shouldn't
-			 * be quoted.
+			 * Don't call escape_json here if it's a valid JSON
+			 * number. Numeric output should usually be a valid 
+			 * JSON number and JSON numbers shouldn't be quoted. 
+			 * Quote cases like "Nan" and "Infinity", however.
 			 */
+			if (strpbrk(outputstr,NON_NUMERIC_LETTER) == NULL)
+				appendStringInfoString(result, outputstr);
+			else
+				escape_json(result, outputstr);
+			pfree(outputstr);
+			break;
+		case TYPCATEGORY_JSON:
+			/* JSON will already be escaped */
+			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
 			break;
@@ -806,9 +820,10 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
 					  typalign, &elements, &nulls,
 					  &nitems);
 
-	/* can't have an array of arrays, so this is the only special case here */
 	if (element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
+	else if (element_type == JSONOID)
+		tcategory = TYPCATEGORY_JSON;
 	else
 		tcategory = TypeCategory(element_type);
 
@@ -876,6 +891,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
+		else if (tupdesc->attrs[i]->atttypid == JSONOID)
+			tcategory = TYPCATEGORY_JSON;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
 
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index 2b573511139c43be3ed469e94ddafb751432e73f..fa8415cdb77c5a5e6adac2084b2bb53e9389bc54 100644
--- a/src/test/regress/expected/json.out
+++ b/src/test/regress/expected/json.out
@@ -367,3 +367,33 @@ SELECT row_to_json(row((select array_agg(x) as d from generate_series(5,10) x)),
  {"f1":[5,6,7,8,9,10]}
 (1 row)
 
+-- non-numeric output
+SELECT row_to_json(q)
+FROM (SELECT 'NaN'::float8 AS "float8field") q;
+      row_to_json      
+-----------------------
+ {"float8field":"NaN"}
+(1 row)
+
+SELECT row_to_json(q)
+FROM (SELECT 'Infinity'::float8 AS "float8field") q;
+        row_to_json         
+----------------------------
+ {"float8field":"Infinity"}
+(1 row)
+
+SELECT row_to_json(q)
+FROM (SELECT '-Infinity'::float8 AS "float8field") q;
+         row_to_json         
+-----------------------------
+ {"float8field":"-Infinity"}
+(1 row)
+
+-- json input
+SELECT row_to_json(q)
+FROM (SELECT '{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}'::json AS "jsonfield") q;
+                           row_to_json                            
+------------------------------------------------------------------
+ {"jsonfield":{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}}
+(1 row)
+
diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql
index 61273555aae1fe874d19e955dc17b115d0f3140e..ab1c41c1c4c98562c25fef87296696507dd9ac6c 100644
--- a/src/test/regress/sql/json.sql
+++ b/src/test/regress/sql/json.sql
@@ -97,3 +97,18 @@ SELECT row_to_json(q,true)
 FROM rows q;
 
 SELECT row_to_json(row((select array_agg(x) as d from generate_series(5,10) x)),false);
+
+-- non-numeric output
+SELECT row_to_json(q)
+FROM (SELECT 'NaN'::float8 AS "float8field") q;
+
+SELECT row_to_json(q)
+FROM (SELECT 'Infinity'::float8 AS "float8field") q;
+
+SELECT row_to_json(q)
+FROM (SELECT '-Infinity'::float8 AS "float8field") q;
+
+-- json input
+SELECT row_to_json(q)
+FROM (SELECT '{"a":1,"b": [2,3,4,"d","e","f"],"c":{"p":1,"q":2}}'::json AS "jsonfield") q;
+