diff --git a/changelogs/unreleased/encode-decimals-as-json-num.md b/changelogs/unreleased/encode-decimals-as-json-num.md
new file mode 100644
index 0000000000000000000000000000000000000000..8621fcfdd26cfaf423491079f97cd785a29f5fd7
--- /dev/null
+++ b/changelogs/unreleased/encode-decimals-as-json-num.md
@@ -0,0 +1,4 @@
+## bugfix/sql
+* Add `encode_decimal_as_number` parameter to JSON config.
+That forces to encode `decimal` as JSON number to force type consistency in JSON output.
+Use with catious - most of JSON parsers assume that number is restricted to float64.
diff --git a/src/lua/serializer.c b/src/lua/serializer.c
index 7c350a3ad4cd8df79fc49a9f41f06506f486d77b..466546b6f88a8c35299c81945952445695c26062 100644
--- a/src/lua/serializer.c
+++ b/src/lua/serializer.c
@@ -73,6 +73,7 @@ static struct {
 	OPTION(LUA_TBOOLEAN, encode_deep_as_nil, 0),
 	OPTION(LUA_TBOOLEAN, encode_invalid_numbers, 1),
 	OPTION(LUA_TNUMBER,  encode_number_precision, 14),
+	OPTION(LUA_TBOOLEAN,  encode_decimal_as_number, 0),
 	OPTION(LUA_TBOOLEAN, encode_load_metatables, 1),
 	OPTION(LUA_TBOOLEAN, encode_use_tostring, 0),
 	OPTION(LUA_TBOOLEAN, encode_invalid_as_nil, 0),
diff --git a/src/lua/serializer.h b/src/lua/serializer.h
index 7ba85a051e876a136ab4c9b0144713846cbce172..f14979961f5a9904323e82fe30482737717288b2 100644
--- a/src/lua/serializer.h
+++ b/src/lua/serializer.h
@@ -118,6 +118,11 @@ struct luaL_serializer {
 	int encode_invalid_numbers;
 	/** Floating point numbers precision (YAML, CJSON only) */
 	int encode_number_precision;
+	/**
+	 * Encode decimal as JSON number instead of string
+	 * potentially losing precision on decoding)
+	 */
+	int encode_decimal_as_number;
 
 	/**
 	 * Enables __serialize meta-value checking:
diff --git a/test/app-tap/json.test.lua b/test/app-tap/json.test.lua
index 7a967513cc20e56a2ef7f116c2172a8e368577a3..e28ec2736da2680461386118a1fdaac349061354 100755
--- a/test/app-tap/json.test.lua
+++ b/test/app-tap/json.test.lua
@@ -25,7 +25,7 @@ test:plan(1)
 
 test:test("json", function(test)
     local serializer = require('json')
-    test:plan(58)
+    test:plan(61)
 
     test:test("unsigned", common.test_unsigned, serializer)
     test:test("signed", common.test_signed, serializer)
@@ -76,6 +76,20 @@ test:test("json", function(test)
     test:is(serializer.cfg.encode_number_precision, orig_encode_number_precision,
             'global option remains unchanged')
 
+    local orig_encode_decimal_as_number =
+        serializer.cfg.encode_decimal_as_number
+    local dec_num = require('decimal').new('0.123456789123456789')
+    serializer.cfg({encode_decimal_as_number = true})
+    test:ok(serializer.encode({a = dec_num}) == '{"a":0.123456789123456789}',
+            'decimal is encoded as number')
+    serializer.cfg({encode_decimal_as_number = orig_encode_decimal_as_number})
+    test:ok(serializer.encode({a = dec_num}) == '{"a":"0.123456789123456789"}',
+            'decimal is encoded as string')
+    test:is(
+        serializer.cfg.encode_decimal_as_number,
+        orig_encode_decimal_as_number,
+        'global option remains unchanged')
+
     local orig_decode_invalid_numbers = serializer.cfg.decode_invalid_numbers
     serializer.cfg({decode_invalid_numbers = false})
     test:ok(not pcall(serializer.decode, '{"a":inf}'),
diff --git a/third_party/lua-cjson/lua_cjson.c b/third_party/lua-cjson/lua_cjson.c
index cd4293df940dd88af240f26f74e491636291e167..94a7a37ca08c6161cf0abb17fa2b5db79d593a83 100644
--- a/third_party/lua-cjson/lua_cjson.c
+++ b/third_party/lua-cjson/lua_cjson.c
@@ -220,6 +220,32 @@ static void json_append_string(struct luaL_serializer *cfg, strbuf_t *json,
     strbuf_append_char_unsafe(json, '\"');
 }
 
+/* json_append_unescaped_string args:
+ * - lua_State
+ * - JSON strbuf
+ * - String (Lua stack index)
+ *
+ * Returns nothing. Doesn't remove string from Lua stack */
+static void json_append_decimal(struct luaL_serializer *cfg, strbuf_t *json,
+                   decimal_t * decNumber)
+{
+    const char *str = decimal_str(decNumber);
+    const size_t len = strlen(str);
+
+    if (! cfg->encode_decimal_as_number) {
+        return json_append_string(cfg, json, str, len);
+    }
+
+    (void) cfg;
+
+    // append decimal as unescaped string to represent JSON number
+    strbuf_ensure_empty_length(json, len);
+    size_t i;
+    for (i = 0; i < len; i++) {
+        strbuf_append_char_unsafe(json, str[i]);
+    }
+}
+
 static void json_append_data(lua_State *l, struct luaL_serializer *cfg,
                              int current_depth, strbuf_t *json);
 
@@ -383,8 +409,7 @@ static void json_append_data(lua_State *l, struct luaL_serializer *cfg,
         switch (field.ext_type) {
         case MP_DECIMAL:
         {
-            const char *str = decimal_str(field.decval);
-            return json_append_string(cfg, json, str, strlen(str));
+            return json_append_decimal(cfg, json, field.decval);
         }
         case MP_UUID:
             return json_append_string(cfg, json, tt_uuid_str(field.uuidval),