diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 9dca682..0731ca5 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -36,6 +36,7 @@
 #include "commands/alter.h"
 #include "commands/defrem.h"
 #include "commands/event_trigger.h"
+#include "common/string.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "parser/parse_func.h"
@@ -52,6 +53,8 @@ static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 									 HeapTuple tup, Relation relMap);
 static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
 									 HeapTuple tup, Relation relMap);
+static DefElem *buildDefItem(const char *name, const char *val,
+							 bool force_strings);
 
 
 /* --------------------- TS Parser commands ------------------------ */
@@ -566,7 +569,7 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 	if (isnull)
 		dictoptions = NIL;
 	else
-		dictoptions = deserialize_deflist(opt);
+		dictoptions = deserialize_deflist(opt, false);
 
 	/*
 	 * Modify the options list as per specified changes
@@ -1519,9 +1522,6 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
  * For the convenience of pg_dump, the output is formatted exactly as it
  * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
  * same options.
- *
- * Note that we assume that only the textual representation of an option's
- * value is interesting --- hence, non-string DefElems get forced to strings.
  */
 text *
 serialize_deflist(List *deflist)
@@ -1539,19 +1539,30 @@ serialize_deflist(List *deflist)
 
 		appendStringInfo(&buf, "%s = ",
 						 quote_identifier(defel->defname));
-		/* If backslashes appear, force E syntax to determine their handling */
-		if (strchr(val, '\\'))
-			appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
-		appendStringInfoChar(&buf, '\'');
-		while (*val)
+
+		/*
+		 * If the value is a T_Integer or T_Float, emit it without quotes,
+		 * otherwise with quotes.  This is essential to allow correct
+		 * reconstruction of the node type as well as the value.
+		 */
+		if (IsA(defel->arg, Integer) || IsA(defel->arg, Float))
+			appendStringInfoString(&buf, val);
+		else
 		{
-			char		ch = *val++;
+			/* If backslashes appear, force E syntax to quote them safely */
+			if (strchr(val, '\\'))
+				appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
+			appendStringInfoChar(&buf, '\'');
+			while (*val)
+			{
+				char		ch = *val++;
 
-			if (SQL_STR_DOUBLE(ch, true))
+				if (SQL_STR_DOUBLE(ch, true))
+					appendStringInfoChar(&buf, ch);
 				appendStringInfoChar(&buf, ch);
-			appendStringInfoChar(&buf, ch);
+			}
+			appendStringInfoChar(&buf, '\'');
 		}
-		appendStringInfoChar(&buf, '\'');
 		if (lnext(deflist, l) != NULL)
 			appendStringInfoString(&buf, ", ");
 	}
@@ -1566,10 +1577,12 @@ serialize_deflist(List *deflist)
  *
  * This is also used for prsheadline options, so for backward compatibility
  * we need to accept a few things serialize_deflist() will never emit:
- * in particular, unquoted and double-quoted values.
+ * in particular, unquoted and double-quoted strings.  Also, for prsheadline
+ * we want to force all the output nodes to be plain strings, whereas for
+ * true deserialization we want to reconstruct the node type.
  */
 List *
-deserialize_deflist(Datum txt)
+deserialize_deflist(Datum txt, bool force_strings)
 {
 	text	   *in = DatumGetTextPP(txt);	/* in case it's toasted */
 	List	   *result = NIL;
@@ -1694,8 +1707,9 @@ deserialize_deflist(Datum txt)
 					{
 						*wsptr++ = '\0';
 						result = lappend(result,
-										 makeDefElem(pstrdup(workspace),
-													 (Node *) makeString(pstrdup(startvalue)), -1));
+										 buildDefItem(workspace,
+													  startvalue,
+													  true));
 						state = CS_WAITKEY;
 					}
 				}
@@ -1726,8 +1740,9 @@ deserialize_deflist(Datum txt)
 					{
 						*wsptr++ = '\0';
 						result = lappend(result,
-										 makeDefElem(pstrdup(workspace),
-													 (Node *) makeString(pstrdup(startvalue)), -1));
+										 buildDefItem(workspace,
+													  startvalue,
+													  true));
 						state = CS_WAITKEY;
 					}
 				}
@@ -1741,8 +1756,9 @@ deserialize_deflist(Datum txt)
 				{
 					*wsptr++ = '\0';
 					result = lappend(result,
-									 makeDefElem(pstrdup(workspace),
-												 (Node *) makeString(pstrdup(startvalue)), -1));
+									 buildDefItem(workspace,
+												  startvalue,
+												  force_strings));
 					state = CS_WAITKEY;
 				}
 				else
@@ -1760,8 +1776,9 @@ deserialize_deflist(Datum txt)
 	{
 		*wsptr++ = '\0';
 		result = lappend(result,
-						 makeDefElem(pstrdup(workspace),
-									 (Node *) makeString(pstrdup(startvalue)), -1));
+						 buildDefItem(workspace,
+									  startvalue,
+									  force_strings));
 	}
 	else if (state != CS_WAITKEY)
 		ereport(ERROR,
@@ -1773,3 +1790,35 @@ deserialize_deflist(Datum txt)
 
 	return result;
 }
+
+/*
+ * Build one DefElem for deserialize_deflist
+ */
+static DefElem *
+buildDefItem(const char *name, const char *val, bool force_strings)
+{
+	if (!force_strings && val[0] != '\0')
+	{
+		int			v;
+		char	   *endptr;
+
+		/* Try to parse as an integer */
+		errno = 0;
+		v = strtoint(val, &endptr, 10);
+		if (errno == 0 && *endptr == '\0')
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeInteger(v),
+							   -1);
+		/* Nope, how about as a float? */
+		errno = 0;
+		(void) strtod(val, &endptr);
+		if (errno == 0 && *endptr == '\0')
+			return makeDefElem(pstrdup(name),
+							   (Node *) makeFloat(pstrdup(val)),
+							   -1);
+	}
+	/* Just make it a string */
+	return makeDefElem(pstrdup(name),
+					   (Node *) makeString(pstrdup(val)),
+					   -1);
+}
diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c
index 88005c0..85851da 100644
--- a/src/backend/tsearch/wparser.c
+++ b/src/backend/tsearch/wparser.c
@@ -329,7 +329,7 @@ ts_headline_byid_opt(PG_FUNCTION_ARGS)
 				VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in));
 
 	if (opt)
-		prsoptions = deserialize_deflist(PointerGetDatum(opt));
+		prsoptions = deserialize_deflist(PointerGetDatum(opt), true);
 	else
 		prsoptions = NIL;
 
@@ -400,7 +400,7 @@ ts_headline_jsonb_byid_opt(PG_FUNCTION_ARGS)
 	state->prsobj = lookup_ts_parser_cache(state->cfg->prsId);
 	state->query = query;
 	if (opt)
-		state->prsoptions = deserialize_deflist(PointerGetDatum(opt));
+		state->prsoptions = deserialize_deflist(PointerGetDatum(opt), true);
 	else
 		state->prsoptions = NIL;
 
@@ -477,7 +477,7 @@ ts_headline_json_byid_opt(PG_FUNCTION_ARGS)
 	state->prsobj = lookup_ts_parser_cache(state->cfg->prsId);
 	state->query = query;
 	if (opt)
-		state->prsoptions = deserialize_deflist(PointerGetDatum(opt));
+		state->prsoptions = deserialize_deflist(PointerGetDatum(opt), true);
 	else
 		state->prsoptions = NIL;
 
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 1641271..8900ac2 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -334,7 +334,7 @@ lookup_ts_dictionary_cache(Oid dictId)
 			if (isnull)
 				dictoptions = NIL;
 			else
-				dictoptions = deserialize_deflist(opt);
+				dictoptions = deserialize_deflist(opt, false);
 
 			entry->dictData =
 				DatumGetPointer(OidFunctionCall1(template->tmplinit,
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 5cd6975..1bd5de7 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -127,7 +127,7 @@ extern void RemoveTSConfigurationById(Oid cfgId);
 extern ObjectAddress AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
 
 extern text *serialize_deflist(List *deflist);
-extern List *deserialize_deflist(Datum txt);
+extern List *deserialize_deflist(Datum txt, bool force_strings);
 
 /* commands/foreigncmds.c */
 extern ObjectAddress AlterForeignServerOwner(const char *name, Oid newOwnerId);
