Andrew Tipton.
        </entry>
        <entry><type>json</type></entry>
        <entry>
-         Returns JSON object pointed to by <parameter>path_elems</parameter>.
+         Returns JSON value pointed to by <parameter>path_elems</parameter>.
        </entry>
        <entry><literal>json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4')</literal></entry>
        <entry><literal>{"f5":99,"f6":"foo"}</literal></entry>
        </entry>
        <entry><type>text</type></entry>
        <entry>
-         Returns JSON object pointed to by <parameter>path_elems</parameter>.
+         Returns JSON value pointed to by <parameter>path_elems</parameter>.
        </entry>
        <entry><literal>json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4', 'f6')</literal></entry>
        <entry><literal>foo</literal></entry>
        </entry>
        <entry><type>SETOF json</type></entry>
        <entry>
-         Expands a JSON array to a set of JSON elements.
+         Expands a JSON array to a set of JSON values.
        </entry>
        <entry><literal>json_array_elements('[1,true, [2,false]]')</literal></entry>
        <entry>
 </programlisting>
        </entry>
       </row>
+      <row>
+       <entry>
+         <indexterm>
+          <primary>json_typeof</primary>
+         </indexterm>
+         <literal>json_typeof(json)</literal>
+       </entry>
+       <entry><type>text</type></entry>
+       <entry>
+         Returns the type of the outermost JSON value as a text string.  The types are
+         <literal>object</>, <literal>array</>, <literal>string</>, <literal>number</>,
+         <literal>boolean</>, and <literal>null</>.  (See note below regarding the
+         distinction between a JSON <literal>null</> and a SQL NULL.)
+       </entry>
+       <entry><literal>json_typeof('-123.4')</literal></entry>
+       <entry><literal>number</literal></entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
     </para>
   </note>
 
+  <note>
+    <para>
+      The <literal>json_typeof</> function's <literal>null</> return value should not be confused
+      with a SQL NULL.  While calling <literal>json_typeof('null'::json)</> will return <literal>null</>,
+      calling <literal>json_typeof(NULL::json)</> will return a SQL NULL.
+    </para>
+  </note>
+
   <para>
     See also <xref linkend="functions-aggregate"> about the aggregate
     function <function>json_agg</function> which aggregates record
 
        }
        appendStringInfoCharMacro(buf, '\"');
 }
+
+/*
+ * SQL function json_typeof(json) -> text
+ *
+ * Returns the type of the outermost JSON value as TEXT.  Possible types are
+ * "object", "array", "string", "number", "boolean", and "null".
+ * 
+ * Performs a single call to json_lex() to get the first token of the supplied
+ * value.  This initial token uniquely determines the value's type.  As our
+ * input must already have been validated by json_in() or json_recv(), the
+ * initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
+ * JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
+ */
+Datum
+json_typeof(PG_FUNCTION_ARGS)
+{
+    text       *json = PG_GETARG_TEXT_P(0);
+
+    JsonLexContext *lex = makeJsonLexContext(json, false);
+    JsonTokenType tok;
+    char *type;
+
+    /* Lex exactly one token from the input and check its type. */
+    json_lex(lex);
+    tok = lex_peek(lex);
+    switch (tok)
+    {
+        case JSON_TOKEN_OBJECT_START:
+            type = "object";
+            break;
+        case JSON_TOKEN_ARRAY_START:
+            type = "array";
+            break;
+        case JSON_TOKEN_STRING:
+            type = "string";
+            break;
+        case JSON_TOKEN_NUMBER:
+            type = "number";
+            break;
+        case JSON_TOKEN_TRUE:
+        case JSON_TOKEN_FALSE:
+            type = "boolean";
+            break;
+        case JSON_TOKEN_NULL:
+            type = "null";
+            break;
+        default:
+            elog(ERROR, "unexpected json token: %d", tok);
+    }
+
+    PG_RETURN_TEXT_P(cstring_to_text(type));
+}
 
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201310091
+#define CATALOG_VERSION_NO     201310101
 
 #endif
 
 DESCR("get record fields from a json object");
 DATA(insert OID = 3961 (  json_populate_recordset  PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 114 16" _null_ _null_ _null_ _null_ json_populate_recordset _null_ _null_ _null_ ));
 DESCR("get set of records with fields from a json array of objects");
+DATA(insert OID = 3968 (  json_typeof              PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "114" _null_ _null_ _null_ _null_ json_typeof _null_ _null_ _null_ ));
+DESCR("get the type of a json value");
 
 /* uuid */
 DATA(insert OID = 2952 (  uuid_in                 PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));
 
 
 extern void escape_json(StringInfo buf, const char *str);
 
+extern Datum json_typeof(PG_FUNCTION_ARGS);
+
 /* functions in jsonfuncs.c */
 extern Datum json_object_field(PG_FUNCTION_ARGS);
 extern Datum json_object_field_text(PG_FUNCTION_ARGS);
 
  null \u0000 escape
 (1 row)
 
+--json_typeof() function
+select value, json_typeof(value)
+  from (values (json '123.4'), 
+               (json '-1'), 
+               (json '"foo"'), 
+               (json 'true'), 
+               (json 'false'), 
+               (json 'null'),
+               (json '[1, 2, 3]'), 
+               (json '[]'), 
+               (json '{"x":"foo", "y":123}'), 
+               (json '{}'), 
+               (NULL::json))
+      as data(value);
+        value         | json_typeof 
+----------------------+-------------
+ 123.4                | number
+ -1                   | number
+ "foo"                | string
+ true                 | boolean
+ false                | boolean
+ null                 | null
+ [1, 2, 3]            | array
+ []                   | array
+ {"x":"foo", "y":123} | object
+ {}                   | object
+                      | 
+(11 rows)
+
 
  null \u0000 escape
 (1 row)
 
+--json_typeof() function
+select value, json_typeof(value)
+  from (values (json '123.4'), 
+               (json '-1'), 
+               (json '"foo"'), 
+               (json 'true'), 
+               (json 'false'), 
+               (json 'null'),
+               (json '[1, 2, 3]'), 
+               (json '[]'), 
+               (json '{"x":"foo", "y":123}'), 
+               (json '{}'), 
+               (NULL::json))
+      as data(value);
+        value         | json_typeof 
+----------------------+-------------
+ 123.4                | number
+ -1                   | number
+ "foo"                | string
+ true                 | boolean
+ false                | boolean
+ null                 | null
+ [1, 2, 3]            | array
+ []                   | array
+ {"x":"foo", "y":123} | object
+ {}                   | object
+                      | 
+(11 rows)
+
 
 select json '{ "a":  "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
 select json '{ "a":  "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
 select json '{ "a":  "null \u0000 escape" }' ->> 'a' as not_unescaped;
+
+--json_typeof() function
+select value, json_typeof(value)
+  from (values (json '123.4'), 
+               (json '-1'), 
+               (json '"foo"'), 
+               (json 'true'), 
+               (json 'false'), 
+               (json 'null'),
+               (json '[1, 2, 3]'), 
+               (json '[]'), 
+               (json '{"x":"foo", "y":123}'), 
+               (json '{}'), 
+               (NULL::json))
+      as data(value);