JavaScript serialize
Returns a string representation of variable (which can later be unserialized)
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 | function serialize (mixed_value) { // Returns a string representation of variable (which can later be unserialized) // // version: 910.813 // discuss at: http://phpjs.org/functions/serialize // + original by: Arpad Ray (mailto:arpad@php.net) // + improved by: Dino // + bugfixed by: Andrej Pavlovic // + bugfixed by: Garagoth // + input by: DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html) // + bugfixed by: Russell Walker (http://www.nbill.co.uk/) // + bugfixed by: Jamie Beck (http://www.terabit.ca/) // + input by: Martin (http://www.erlenwiese.de/) // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // - depends on: utf8_encode // % note: We feel the main purpose of this function should be to ease the transport of data between php & js // % note: Aiming for PHP-compatibility, we have to translate objects to arrays // * example 1: serialize(['Kevin', 'van', 'Zonneveld']); // * returns 1: 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}' // * example 2: serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}); // * returns 2: 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}' var _getType = function (inp) { var type = typeof inp, match; var key; if (type == 'object' && !inp) { return 'null'; } if (type == "object") { if (!inp.constructor) { return 'object'; } var cons = inp.constructor.toString(); match = cons.match(/(\w+)\(/); if (match) { cons = match[1].toLowerCase(); } var types = ["boolean", "number", "string", "array"]; for (key in types) { if (cons == types[key]) { type = types[key]; break; } } } return type; }; var type = _getType(mixed_value); var val, ktype = ''; switch (type) { case "function": val = ""; break; case "boolean": val = "b:" + (mixed_value ? "1" : "0"); break; case "number": val = (Math.round(mixed_value) == mixed_value ? "i" : "d") + ":" + mixed_value; break; case "string": mixed_value = this.utf8_encode(mixed_value); val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\""; break; case "array": case "object": val = "a"; /* if (type == "object") { var objname = mixed_value.constructor.toString().match(/(\w+)\(\)/); if (objname == undefined) { return; } objname[1] = this.serialize(objname[1]); val = "O" + objname[1].substring(1, objname[1].length - 1); } */ var count = 0; var vals = ""; var okey; var key; for (key in mixed_value) { ktype = _getType(mixed_value[key]); if (ktype == "function") { continue; } okey = (key.match(/^[0-9]+$/) ? parseInt(key, 10) : key); vals += this.serialize(okey) + this.serialize(mixed_value[key]); count++; } val += ":" + count + ":{" + vals + "}"; break; case "undefined": // Fall-through default: // if the JS object has a property which contains a null value, the string cannot be unserialized by PHP val = "N"; break; } if (type != "object" && type != "array") { val += ";"; } return val; } |
Examples
» Example 1
Running
1 | serialize(['Kevin', 'van', 'Zonneveld']); |
Should return
1 | 'a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}' |
» Example 2
Running
1 | serialize({firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}); |
Should return
1 | 'a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}' |
Dependencies
In order to use this function, you also need:
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have serialize goodness in JavaScript.
@Russell Walker: That sounds good, but can you please confirm that this is in fact the same behavior in PHP?
When serializing strings that contain URL entities (such as the plus symbol), they were being lost during unserialization in PHP. To fix this, I changed line 58 to URIEncode the string value like this:
1 | val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + encodeURIComponent(mixed_value) + "\""; |
@ Alexandre Felipe Muller: Thanks for sharing. It's gonna take a while to investigate it and strip it of your environment's specific dependencies like showMessage(Element('cc_msg_err_serialize_data_unknown').value);
If after that all the testcases pass and they're indeed faster I will replace the current implementations with your's
We're using my serialize and unserialize in my project for 3 years, acording to my tests it's 3 or 4 times faster. Who want to see
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110111 112 113 114 115116 117 118 119 120121 122 123 124 125126 127 128 129 130131 132 133 134 135136 137 138 139 140141 142 143 144 145146 147 148 149 150151 152 153 154 155156 157 158 159 160161 162 163 164 165166 167 168 169 170171 172 173 174 | cConnector.prototype.serialize = function(data) { var _thisObject = this; var f = function(data) { var str_data; if (data == null || (typeof(data) == 'string' && data == '')) { str_data = 'N;'; } else switch(typeof(data)) { case 'object': var arrayCount = 0; str_data = ''; for (i in data) { if (i == 'length') { continue; } arrayCount++; switch (typeof(i)) { case 'number': str_data += 'i:' + i + ';' + f(data[i]); break; case 'string': str_data += 's:' + i.length + ':"' + i + '";' + f(data[i]); break; default: showMessage(Element('cc_msg_err_serialize_data_unknown').value); break; } } if (!arrayCount) { str_data = 'N;'; } else { str_data = 'a:' + arrayCount + ':{' + str_data + '}'; } break; case 'string': str_data = 's:' + data.length + ':"' + data + '";'; break; case 'number': str_data = 'i:' + data + ';'; break; case 'boolean': str_data = 'b:' + (data ? '1' : '0') + ';'; break; default: showMessage(Element('cc_msg_err_serialize_data_unknown').value); return null; } return str_data; } return f(data); } //Unserialize Data Method cConnector.prototype.unserialize = function(str) { _thisObject = this; var matchB = function (str, iniPos) { var nOpen, nClose = iniPos; do { nOpen = str.indexOf('{', nClose+1); nClose = str.indexOf('}', nClose+1); if (nOpen == -1) { return nClose; } if (nOpen < nClose ) { nClose = matchB(str, nOpen); } } while (nOpen < nClose); return nClose; } var f = function (str) { switch (str.charAt(0)) { case 'a': var data = new Array(); var n = parseInt( str.substring(str.indexOf(':')+1, str.indexOf(':',2) ) ); var arrayContent = str.substring(str.indexOf('{')+1, str.lastIndexOf('}')); for (var i = 0; i < n; i++) { var pos = 0; /* Process Index */ var indexStr = arrayContent.substr(pos, arrayContent.indexOf(';')+1); var index = f(indexStr); pos = arrayContent.indexOf(';', pos)+1; /* Process Content */ var part = null; switch (arrayContent.charAt(pos)) { case 'a': var pos_ = matchB(arrayContent, arrayContent.indexOf('{', pos))+1; part = arrayContent.substring(pos, pos_); pos = pos_; data[index] = f(part); break; case 's': var pval = arrayContent.indexOf(':', pos+2); var val = parseInt(arrayContent.substring(pos+2, pval)); pos = pval + val + 4; data[index] = arrayContent.substr(pval+2, val); break; default: part = arrayContent.substring(pos, arrayContent.indexOf(';', pos)+1); pos = arrayContent.indexOf(';', pos)+1; data[index] = f(part); break; } arrayContent = arrayContent.substr(pos); } break; case 's': var pos = str.indexOf(':', 2); var val = parseInt(str.substring(2,pos)); var data = str.substr(pos+2, val); str = str.substr(pos + 4 + val); break; case 'i': case 'd': var pos = str.indexOf(';'); var data = parseInt(str.substring(2,pos)); str = str.substr(pos + 1); break; case 'N': var data = null; str = str.substr(str.indexOf(';') + 1); break; case 'b': var data = str.charAt(2) == '1' ? true : false; break; } return data; } return f(str); } |
Should not lines 83-84 be as follows for the namespaced version. Otherwise the function cannot be found...
1 2 | vals += this.serialize(okey) + this.serialize(mixed_value[key]); |
I found that if the javascript object has a property which contains a null value, the string cannot be unserialized by PHP. To fix this, I added:
1 2 3 | default: val = "N"; break; |
...to the end of the switch block (around line 92 on the above function). The 'undefined' case should probably be moved down to the bottom as well so both can be handled together, ie:
1 2 3 4 | case 'undefined': default: val = "N"; break; |
@ Russell Walker: Thanks for contributing. I've implemented your fix in svn & it will be available shortly.
http://trac.plutonia.nl/projects/phpjs/browser/trunk/functions/var/serialize.js
I found that when serializing utf-8 characters that differ from iso-8859-1, the string could not be deserialized by PHP. This is because PHP sees the string as containing more characters than it really does (as it thinks 1 character = 1 byte, when unicode characters can take up more than 1 byte). So I amended the code on line 97 from
1 | val = "s:" + mixed_value.length + ":\"" + mixed_value + "\""; |
to
1 | val = "s:" + encodeURIComponent(mixed_value).replace(/%../g, 'x').length + ":\"" + mixed_value + "\""; |
...so now it serializes utf-8 characters in a way that PHP can deserialize. Note however, that this will probably break the javascript unserialize function, as JS and PHP cannot agree on the number of characters in the string.
The code to get the length of the string in bytes came from DtTvB: http://dt.in.th/2008-09-16.string-length-in-bytes.html
@ AndreaZ: Thx for the pastebin, this makes it clear to me what's going wrong. You are stripping slashes before you are unserializing the string, while escaped characters are an essential part of of the serialized object.
To circumvent, remove stripslashes, or first use base64_encode over the serialized object, and then in php decode it.
@ AndreaZ: Why don't you put it in pastebin.org, and add a link here. then we have syntax highlighting as well. Thanx!
@Kevin: i inserted the code inside
1 |
send me an email, so i can send you the test file
@Kevin: that's the test code
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 35 | <html> <body> <?php if (isset($_POST['foo'])) { error_reporting(E_ALL); $ar = $_POST['foo']; var_dump($ar).'<br/>'; $ser=stripslashes($ar); var_dump($ser).'<br/>'; $unser = unserialize($ser); var_dump($unser).'<br/>'; } ?> <script language="javascript" type="text/javascript"><!-- function clicca() { document.adminForm.foo.value = serialize(document.adminForm.foo.value); } function serialize( mixed_value ) { /* your function */ } //--> </script> <form action="try_serialize.php" method="post" name="adminForm"> <textarea id="foo" name="foo"> a textarea with newline</textarea> <button type="sumbit" onclick="clicca();">Send</button> </form> </body> </html> |
@ AndreaZ: Ok if you put the PHP-serialized string inside codeblocks here, I can unserialize it with PHP as well, and then test serializing it with JS.
So if you could provide me with that data that would help me a lot. thx
@Kevin: i serialized a multidimensional array with your javascript function, then i unserialized the result inside a php file: unserialize returns false (if i serialize with php it works)
how can i send to you the test that i made? it is a little php file with inside your javascript serialize function
PS: thanks a lot for your work ;-)
@ AndreaZ: I did some testing with the following code:
1 2 | $ser = serialize("a \n b"); var_dump($ser); |
.. which is executable by both PHP & JS.
Both return the exact same output:
1
2
3
4
56
| kevin@kevin-desktop:~/workspace/plutonia-phpjs/_tools$ rhino debug.js string(12) "s:5:"a b";" kevin@kevin-desktop:~/workspace/plutonia-phpjs/_tools$ php debug.php string(12) "s:5:"a b";" |
..so I'm wondering could it be that something else is buggy in the script you are using? If not, can you supply the full input and code that gives the wrong results?
when there's a newline character (\n) inside a serialized string, php unseriliaze returns false
i don't know why :-(
Works fine with php 5.2.0.
But doesn't work with php 5.2.6 ! Php cannot unserialize the string.
Any known issues about this ?
Hm, an interesting line of code, not sure how it is supposed to work:
1 2 3 | if (ktype == "function" && ktype == "object") { continue; } |
Cheers,
Garagoth.
The returned representation of provided function is valid PHP code (which is correct).
But does anyone have a JS var_export function whose returned representation is valid javascript code?
The returned value type should be string and it could be passed to eval() function.
Examples:
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 | var a = new Array(12, '13', 'abc', 'line1\nline2\nline3'); var js_code = var_export(a); /* the returned value should be: "{0:12, 1:'13', 2:'abc', 3:'line1\nline2\nline3'}"*/ var b = {'key1':4, 'key2':'5', 'key3':'xxx\n123', 555:'text'}; js_code = var_export(b); /*the returned value should be: "{'key1':4, 'key2':'5', 'key3':'xxx\n123', 555:'text'}" */ var c = 123;js_code = var_export(c); // "123" var d = '321'; js_code = var_export(d); // "'321'" var e = 'multilne\ntext'; js_code = var_export(e); // "'multiline\ntext'" function add(x, y) { res = x + y; return res; } var js_code = var_export(add); /*the returned value should be: "function add(x, y) { res = x + y; return res; }" */ |
Thanks.
woops I don't think my code showed up properly.
1 2 3 4 56 7 8 9 1011 | for (key in mixed_value) { var ktype = _getType(mixed_value[key]); //alert(key + ' type is ' + ktype); if (ktype != "function" && ktype != "object") { okey = (key.match(/^[0-9]+$/) ? parseInt(key) : key); vals += serialize(okey) + serialize(mixed_value[key]); count++; } } |
serialize doesn't work well with mootools since mootools adds or extends the array object with functions which serialize picks up on and tries to translate into a string.
At least it broke my code when I included mootools.
I fixed it by having serialize not try to translate objects or functions. It doesn't seem like functions are being handled anyway.
[code lang="javascript"]
case "function":
val = "";
break;
for (key in mixed_value) {
var ktype = _getType(mixed_value[key]);
//alert(key + ' type is ' + ktype);
if (ktype != "function" && ktype != "object") {
okey = (key.match(/^[0-9]+$/) ? parseInt(key) : key);
vals += serialize(okey) +
serialize(mixed_value[key]);
count++;
}
}
Sorry plz, it was my fault.
I used htmlspecialchars($_REQUEST), so the variable with serialized string encoded too.
Function works fine :) thx
@ Ren: Can you please provide a print_r of the array in CODE blocks that you are trying to serialize? We need your import to improve this function. Thanks!
Hallo, thanx for the code. How can I find out in the returned object the length of the arrays (if these arrays be)?
@ Kevin: Arpad Ray's implementation uses "eval" and "eval is evil"(http://blogs.msdn.com/ericlippert/archive/2003/11/01/53329.aspx)
For every other person that needs an unserialize implementation:
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105 | function unserialize(data){ function error(type, msg, filename, line){throw new window[type](msg, filename, line);} function read_until(data, offset, stopchar){ var buf = []; var char = data.slice(offset, offset + 1); var i = 2; while(char != stopchar){ if((i+offset) > data.length){ error('Error', 'Invalid'); } buf.push(char); char = data.slice(offset + (i - 1),offset + i); i += 1; } return [buf.length, buf.join('')]; }; function read_chars(data, offset, length){ buf = []; for(var i = 0;i < length;i++){ var char = data.slice(offset + (i - 1),offset + i); buf.push(char); } return [buf.length, buf.join('')]; }; function _unserialize(data, offset){ if(!offset) offset = 0; var buf = []; var dtype = (data.slice(offset, offset + 1)).toLowerCase(); var dataoffset = offset + 2; var typeconvert = new Function('x', 'return x'); var chars = 0; var datalength = 0; switch(dtype){ case "i": typeconvert = new Function('x', 'return parseInt(x)'); var readData = read_until(data, dataoffset, ';'); var chars = readData[0]; var readdata = readData[1]; dataoffset += chars + 1; break; case "b": typeconvert = new Function('x', 'return (parseInt(x) == 1)'); var readData = read_until(data, dataoffset, ';'); var chars = readData[0]; var readdata = readData[1]; dataoffset += chars + 1; break; case "d": typeconvert = new Function('x', 'return parseFloat(x)'); var readData = read_until(data, dataoffset, ';'); var chars = readData[0]; var readdata = readData[1]; dataoffset += chars + 1; break; case "n": readdata = null; break; case "s": var ccount = read_until(data, dataoffset, ':'); var chars = ccount[0]; var stringlength = ccount[1]; dataoffset += chars + 2; var readData = read_chars(data, dataoffset+1, parseInt(stringlength)); var chars = readData[0]; var readdata = readData[1]; dataoffset += chars + 2; if(chars != parseInt(stringlength) && chars != readdata.length){ error('SyntaxError', 'String length mismatch'); } break; case "a": var readdata = {}; var keyandchars = read_until(data, dataoffset, ':'); var chars = keyandchars[0]; var keys = keyandchars[1]; dataoffset += chars + 2; for(var i = 0;i < parseInt(keys);i++){ var kprops = _unserialize(data, dataoffset); var kchars = kprops[1]; var key = kprops[2]; dataoffset += kchars; var vprops = _unserialize(data, dataoffset); var vchars = vprops[1]; var value = vprops[2]; dataoffset += vchars; readdata[key] = value; } dataoffset += 1; break; default: error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype); break; } return [dtype, dataoffset - offset, typeconvert(readdata)]; }; return _unserialize(data, 0)[2]; } |
Code translated from: http://hurring.com/scott/code/python/serialize/
@ Andrea Giammarchi: Impressive code Andrea! I will look into this and if I use (parts of) it, I will credit you accordingly! Thanks
two years ago, 15.000 users, about zero problems:
http://www.devpro.it/javascript_id_102.html
It's able to save correctly UTF-8 strings as well.
Cheers
@ Doug: About unserialize, just now I found a very good javascript unserialize function by Arpad Ray. I've included the function in this project. If Arpad doesn't approve however (I've sent him an email), we will still have to write it ourselves.
hello,
i d like to serialize a window object by a js var that contain window.open , thus to keep in memory the window open if the php page is refreshed. i tried to use your code but it says js error "too much recursion... any suggestion ? thanks and congratulation for the work done
@ Ates Goral: Example 14 is giving: too much recursion after implementing the new get_class function in serialize
@ Ates Goral: Works like a charm, I will build this in serialize and add it as a dependency.
About your php-strict/javascript-flexible question. I think we should stay with PHP as close as possible. Hopefully this will provide consistency & clarity for end users. And interoperability between php-js-function throughout the project. This approach should also ensure that no extra function documentation has to be written because PHP's function manual will (in most cases) be valid.
Here's get_class(). I think serialize() now can re-use this one instead of the local getObjectClass() implementation.
I've added the extra instanceof checks solely to match PHP behaviour. They can be removed since JavaScript has no problem with getting class names for simple types or arrays/functions etc. This brings up the question: Are we trying to mimic PHP behaviour as closely as possible or is it all right to introduce additional functionality brought forth by the flexibility of JavaScript?
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 25 | function get_class(obj) { // * example 1: get_class(new (function MyClass() {})); // * returns 1: "MyClass" // * example 2: get_class({}); // * returns 2: "Object" // * example 3: get_class([]); // * returns 3: false // * example 4: get_class(42); // * returns 4: false // * example 5: get_class(window); // * returns 5: false // * example 6: get_class(function MyFunction() {}); // * returns 6: false if (obj instanceof Object && !(obj instanceof Array) && !(obj instanceof Function) && obj.constructor) { var arr = obj.constructor.toString().match(/function\s*(\w+)/); if (arr && arr.length == 2) { return arr[1]; } } return false; } |
Hi Kevin,
Here are a few improvements to what I originally had:
For Array detection, instead of:
1 | ("length" in mixed_val) |
it's nicer to say:
1 | (mixed_val instanceof Array) |
Also, an additional check can be added to handle the NaN and Infinite values:
1
2
3
4
56
| case "number": if (mixed_val == NaN || mixed_val == Infinity) { return false; } ... |


Le Torbi
26 Sep '09
when fixing the UTF-8 issue of the unserialize() function I've found a way to improve the speed of the size calculation for strings. It's quite simple and need no complex string operations or regular expressions. Here is the code:
I've made some simple test and it seems that my function needs about 0.0004ms per run, while the old needs 0.004ms. Ok, it's not much, but maybe worth the code anyway...
BTW: What do I have to to to get this into the official code?
Bai
Le Torbi