Serialise BigDecimal, BigNumber, BigInt etc. to and from JSON - javascript

What types, and what serialisation pattern, can I use to ensure I have arbitrary-precision numbers in my JavaScript program while it's running, but the state is serialised to / deserialised from JSON with a minimum of pain?
I am writing an application that makes heavy use of numbers with higher precision than allowed by the ECMAScript native Number type. So I need to use a custom type (currently BigNumber) to represent those values.
I am looking hungrily at the proposed BigInt type, which when implemented as standard will be an improvement for many applications including the one I'm writing. However that won't help at all with JSON, which knows nothing about BigInt any more than BigNumber.
Whichever custom type I choose, there are many values of this type throughout the application state. So much that it's worth considering a custom hook in the serialisation / deserialisation layer that will handle transforming it to / from JSON.
Presumably the JSON document will have to represent the values as JSON native types (e.g. Object or String instances). How then, when de-serialising the entire complex application state, to reliably recognise and de-serialise those instances among all the others, to the correct value of the correct BigInt or BigNumber type?
How can I serialise that state, such that any BigNumber (or insert some other arbitrary-precision number type) value reliably survives the serialisation / de-serialisation process, to correct values of the correct type?

If modifying the type (by adding a property to the prototype) is feasible, there is a toJSON hook from JSON.stringify, designed specifically to help custom types collaborate with JSON serialisation.
If an object being stringified has a property named toJSON whose value is a function, then the toJSON() method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON() method when called will be serialized.
So you could add a new method to the BigNumber class:
BigNumber.prototype.toJSON = function toJSON(key) {
return {
_type: 'BigNumber',
_data: Object.assign({}, this),
};
};
state = {
lorem: true,
ipsum: "Consecteur non dibale",
dolor: new BigNumber(107.58),
sit: { spam: 5, eggs: 6, beans: 7 },
amet: false,
};
serialisedState = JSON.stringify(state);
console.debug("serialisedState:", serialisedState);
// → '{"lorem":true,"ipsum":"Consecteur non dibale","dolor":{"_type":"BigNumber","_data":{"s":1,"e":2,"c":[1,0,7,5,8]}},"sit":{"spam":5,"eggs":6,"beans":7},"amet":false}'
You can then recognise those specific objects when de-serialising, using the reviver parameter of JSON.parse:
If a reviver is specified, the value computed by parsing is transformed before being returned. Specifically, the computed value and all its properties (beginning with the most nested properties and proceeding to the original value itself) are individually run through the reviver. Then it is called, with the object containing the property being processed as this, and with the property name as a string, and the property value as arguments. [If the return value is not undefined], the property is redefined to be the return value.
function reviveFromJSON(key, value) {
let result = value;
if (
(typeof value === 'object' && value !== null)
&& (value.hasOwnProperty('_type'))) {
switch (value._type) {
case 'BigNumber':
result = Object.assign(new BigNumber(0), value._data);
}
}
return result;
}
state = JSON.parse(serialisedState, reviveFromJSON);
console.debug("state:", state);
// → { … dolor: BigNumber { s: 1, e: 2, c: [ 1, 0, 7, 5, 8 ] }, … }

One potential solution is the Granola library for Node.js.
granola provides a JSON compatible stringifier and parser with support for modern language and object primitives.
This would require:
Convert the application from using BigNumber values to BigInt values.
Wait for tools to support BigInt in the entire build tool chain.

Related

what is the main difference between JS object and JSON? [duplicate]

Can someone tell me what is the main difference between a JavaScript object defined by using Object Literal Notation and JSON object?
According to a JavaScript book it says this is an object defined by using Object Notation:
var anObject = {
property1 : true,
showMessage : function (msg) { alert(msg) }
};
Why isn't it a JSON object in this case? Just because it is not defined by using quotation marks?
Lets clarify first what JSON actually is. JSON is a textual, language-independent data-exchange format, much like XML, CSV or YAML.
Data can be stored in many ways, but if it should be stored in a text file and be readable by a computer, it needs to follow some structure. JSON is one of the many formats that define such a structure.
Such formats are typically language-independent, meaning they can be processed by Java, Python, JavaScript, PHP, you name it.
In contrast, JavaScript is a programming language. Of course JavaScript also provides a way to define/describe data, but the syntax is very specific to JavaScript.
As a counter example, Python has the concept of tuples, their syntax is (x, y). JavaScript doesn't have something like this.
Lets look at the syntactical differences between JSON and JavaScript object literals.
JSON has the following syntactical constraints:
Object keys must be strings (i.e. a character sequence enclosed in double quotes ").
The values can be either:
a string
a number
an (JSON) object
an array
true
false
null
Duplicate keys ({"foo":"bar","foo":"baz"}) produce undefined, implementation-specific results; the JSON specification specifically does not define their semantics
In JavaScript, object literals can have
String literals, number literals or identifier names as keys (since ES6, keys can now also be computed, which introduces yet another syntax).
The values can be any valid JavaScript expression, including function definitions and undefined.
Duplicate keys produce defined, specified results (in loose mode, the latter definition replaces the former; in strict mode, it's an error).
Knowing that, just by looking at the syntax, your example is not JSON because of two reasons:
Your keys are not strings (literals). They are identifier names.
You cannot assign a function as a value to a "JSON object" (because JSON doesn't define any syntax for functions).
But most importantly, to repeat my explanation from the beginning: You are in a JavaScript context. You define a JavaScript object. If any, a "JSON object" can only be contained in a string:
var obj = {foo: 42}; // creates a JavaScript object (this is *not* JSON)
var json = '{"foo": 452}'; // creates a string containing JSON
That is, if you're writing JavaScript source code, and not dealing with a string, you're not dealing with JSON. Maybe you received the data as JSON (e.g., via ajax or reading from a file), but once you or a library you're using has parsed it, it's not JSON anymore.
Only because object literals and JSON look similar, it does not mean that you can name them interchangeably. See also There's no such thing as a "JSON Object".
JSON has a much more limited syntax including:
Key values must be quoted
Strings must be quoted with " and not '
You have a more limited range of values (e.g. no functions allowed)
There is really no such thing as a "JSON Object".
The JSON spec is a syntax for encoding data as a string. What people call a "JSON Object" ( in javascript ) is really just an ordinary javascript object that has (probably) been de-serialized from a valid JSON string, and can be easily re-serialized as a valid JSON string. This generally means that it contains only data ( and not functions ). It also means that there are no dates, because JSON does not have a date type ( probably the most painful thing about JSON ;)
Furthermore, (side-rant...) when people talk about a "JSON Object", they almost always mean data that has the "curly-braces" at the top-level. This corresponds nicely to a javascript object. However, the JSON spec does not require that there be a single "curly-braces" object at the top-level of a JSON string. It is perfectly valid JSON to have a list at the top-level, or even to have just a single value. So, while every "JSON Object" corresponds to valid JSON, not all valid JSON strings correspond to what we would call a "JSON Object"! ( because the string could represent a list or an atomic value )
According to JSON in JavaScript,
JSON is a subset of the object
literal notation of JavaScript.
In other words, valid JSON is also valid JavaScript object literal notation but not necessarily the other way around.
In addition to reading the documentation, as #Filix King suggested, I also suggest playing around with the JSONLint online JSON validator. That's how I learned that the keys of JSON objects must be strings.
🔫 JSON: The Fat-Free Alternative to XML
JSON has been widely adopted by people who found that it made it a lot easier to produce distributed applications and services. The official Internet media type for JSON is application/json RFC 4627. JSON filenames use the extension .json.
► JavaScript Object Notation (JSON) is a lightweight, text-based, language-independent data interchange format. JSON has been used to exchange data between applications written in any Programming language.
The JSON object is a single object that contains two functions, parse and stringify, that are used to parse and construct JSON texts.
JSON.stringify produces a String that conforms to the following JSON grammar.
JSON.parse accepts a String that conforms to the JSON grammar.
The parseJSON method will be included in the Fourth Edition of ECMAScript. In the meantime, a JavaScript implementation is available at json.org.
var objLiteral = {foo: 42}; // JavaScript Object
console.log('Object Literal : ', objLiteral ); // Object {foo: 42}foo: 42__proto__: Object
// This is a JSON String, like what you'd get back from an AJAX request.
var jsonString = '{"foo": 452}';
console.log('JOSN String : ', jsonString ); // {"foo": 452}
// This is how you deserialize that JSON String into an Object.
var serverResposnceObject = JSON.parse( jsonString );
console.log('Converting Ajax response to JavaScript Object : ', serverResposnceObject); // Object {foo: 42}foo: 42 __proto__: Object
// And this is how you serialize an Object into a JSON String.
var serverRequestJSON = JSON.stringify( objLiteral );
console.log('Reqesting server with JSON Data : ', serverRequestJSON); // '{"foo": 452}'
JSON is subset of JavaScript. Javascript was derived from the ECMAScript Programming Language Standard.
► ECMAScript
ECMAScript has grown to be one of the world's most widely used general purpose programming languages. It is best known as the language embedded in web browsers but has also been widely adopted for server and embedded applications. ECMAScript is based on several originating technologies, the most well-known being JavaScript (Netscape Communications)) and JScript (Microsoft Corporation).). Though before 1994, ECMA was known as "European Computer Manufacturers Association", after 1994, when the organization became global, the "trademark" "Ecma" was kept for historical reasons.
ECMAScript is the language, whereas JavaScript, JScript, and even ActionScript are called "Dialects".
Dialects have been derived from the same language. They are are quite similar to each other as they have been derived from the same language but they have undergone some changes.
A dialect is a variation in the language itself. It is derived from a single language.
SQL Language - Hibernate MySQL Dialect, Oracle Dialect,.. which have some changes or added functionality.
Information about the browser and computer of your users.
navigator.appName // "Netscape"
ECMAScript is the scripting language that forms the basis of JavaScript. JavaScript language resources.
ECMA-262 Links
Initial Edition, June 1997
PDF.
2nd Edition, August 1998
PDF.
3rd Edition, December 1999 PDF.
5th Edition, December 2009 PDF.
5.1 Edition, June 2011 HTML.
6th Edition, June 2015 HTML.
7ᵗʰ Edition, June 2016 HTML.
8th edition, June 2017 HTML.
9th Edition, 2018 HTML.
NOTE « 4th edition of ECMAScript not published as the work was incomplete.
JSON defines a small set of formatting rules for the portable representation of structured data.
► Key values must be quoted, only Strings are allowed for keys. If you use other than String it will convert to String. But not recommended to use keys other than String's. Check an example like this - { 'key':'val' } over RFC 4627 - jsonformatter
var storage = {
0 : null,
1 : "Hello"
};
console.log( storage[1] ); // Hello
console.log( JSON.stringify( storage ) ); // {"0":null,"1":"Hello","2":"world!"}
var objLiteral = {'key1':'val1'};
var arr = [10, 20], arr2 = [ 'Yash', 'Sam' ];
var obj = { k: 'v' }, obj2 = { k2: 'v2' };
var fun = function keyFun() {} ;
objLiteral[ arr ] = 'ArrayVal'; objLiteral[ arr2 ] = 'OverridenArrayVal';
objLiteral[ obj ] = 'ObjectVal'; objLiteral[ obj2 ] = 'OverridenObjectVal';
objLiteral[ fun ] = 'FunctionVal';
console.log( objLiteral );
// Object {key1: "val1", 10,20: "ArrayVal", Yash,Sam: "OverridenArrayVal", [object Object]: "OverridenObjectVal", function keyFun() {}: "FunctionVal"}
console.log( JSON.stringify( objLiteral ) );
// {"key1":"val1","10,20":"ArrayVal","Yash,Sam":"OverridenArrayVal","[object Object]":"OverridenObjectVal","function keyFun() {}":"FunctionVal"}
console.log( JSON.parse( JSON.stringify( objLiteral ) ) );
// Object {key1: "val1", 10,20: "ArrayVal", Yash,Sam: "OverridenArrayVal", [object Object]: "OverridenObjectVal", function keyFun() {}: "FunctionVal"}
console.log('Accessing Array Val : ', objLiteral[ [10,20] ] );
console.log('Accessing Object Val : ', objLiteral[ '[object Object]' ] );
console.log('Accessing Function Val : ', objLiteral[ 'function keyFun() {}' ] );
► JSON Strings must be quoted with " and not '. A string is very much like a C or Java string. Strings should be wrapped in double quotes.
Literals are fixed values, not variables, that you literally provide in your script.
A string is a sequence of zero or more characters wrapped in quotes with backslash escapement, the same notation used in most programming languages.
🔫 - Special Symbols are allowed in String but not recomended to use.
" - Special characters can be escaped. But not recomended to escape (') Single Quotes.
In Strict mode it will throw and Error - SyntaxError: Unexpected token ' in JSON
Check with this code { "Hai\" \n Team 🔫":5, "Bye \'": 7 } over online JSON Edtions. Modes notStrict, Strinct.
var jsonString = "{'foo': 452}"; // {'foo': 452}
var jsonStr = '{"foo": 452}'; // {"foo": 452}
JSON.parse( jsonString ); // Unexpected token ' in JSON at position 1(…)
JSON.parse( jsonStr ); // Object {foo: 452}
objLiteral['key'] = 'val'; // Object {foo: 42, key: "val"}
objLiteral.key2 = 'val';
// objLiteral.key\n3 - SyntaxError: Invalid or unexpected token
objLiteral['key\n3'] = 'val'; // Object {"foo": "42", key: "val", key2: "val", "key↵3": "val"}
JSON.stringify( objLiteral ); // {"foo":"42","key":"val","key2":"val","key\n3":"val"}
Object Property accessors provide access to an object's properties by using the dot notation or the bracket notation.
► You have a more limited range of values (e.g. no functions allowed). A value can be a string in double quotes, number, boolean, null, object, or array. These structures can be nested.
var objLiteral = {};
objLiteral.funKey = function sayHello() {
console.log('Object Key with function as value - Its outcome message.');
};
objLiteral['Key'] = 'Val';
console.log('Object Literal Fun : ', objLiteral );
// Object Literal Fun : Object {Key: "Val"}Key: "Val"funKey: sayHello()__proto__: Object
console.log( JSON.stringify( objLiteral ) ); // {"Key":"Val"}
► JavaScript is the most popular implementation of the ECMAScript Standard.
The core features of Javascript are based on the ECMAScript standard, but Javascript also has other additional features that are not in the ECMA specifications/standard. Every browser has a JavaScript interpreter.
JavaScript is a dynamically typed language. That means you don't have to specify the data type of a variable when you declare it, and data types are converted automatically as needed during script execution.
Literals :
'37' - 7 // 30
'37' + 7 // "377"
+'37' + 7 // 44
+'37' // 37
'37' // "37"
parseInt('37'); // 37
parseInt('3.7'); // 3
parseFloat(3.7); // 3.7
// An alternative method of retrieving a number from a string is with the + (unary plus) operator:
+'3.7' // 3.7
Object literals RFC 7159
An object structure is represented as a pair of curly brackets surrounding zero or more name/value pairs (or members). A name is a
string. A single colon comes after each name, separating the name from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique.
ECMAScript supports prototype-based inheritance. Every constructor has an associated prototype, and every object created by that constructor has an implicit reference to the prototype (called the object’s
prototype) associated with its constructor. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain.
In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behavior. In ECMAScript, the state and methods are carried by objects, and structure, behavior, and state are all inherited.
A prototype is an object used to implement structure, state, and behavior inheritance in ECMAScript. When a constructor creates an object, that object implicitly references the constructor’s associated prototype for the purpose of resolving property references. The constructor’s associated prototype can
be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype.
As far as I understand the main difference is the flexibility.
JSON is a kind of wrapper on "JavaScript Object Notation" which forces users to obey more strict rules for defining the objects. And it does this by limiting the possible object declaration ways provided by JavaScript Object Notation feature.
As a result we have a simpler and more standardized objects which suits better on data-exchange between platforms.
So basically, the newObject in my example above is an object defined by using JavaScript Objeect Notation; but it is not a 'valid' JSON object because it does not follow the rules that JSON standards require.
This link is also quite helpful:
http://msdn.microsoft.com/en-us/library/bb299886.aspx
For the ones who still think that RFC are more important than blogs and opinion based misconceptions, let's try to answer clarifying some points.
I'm not going to repeat all the correct differences already mentioned in previous answers, here I'm just trying adding value summarizing some crucial part rfc7159
Extracts from https://www.rfc-editor.org/rfc/rfc7159
JavaScript Object Notation (JSON) is a text format for the
serialization of structured data. It is derived from the object
literals of JavaScript, as defined in the ECMAScript Programming
Language Standard, Third Edition [ECMA-262].
JSON can represent four primitive types (strings, numbers, booleans,
and null) and two structured types (objects and arrays).
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
begin-object = ws %x7B ws ; { left curly bracket
end-object = ws %x7D ws ; } right curly bracket
A JSON value MUST be an object, array, number, or string, or one of
the following three literal names: false null true
An object structure is represented as a pair of curly brackets
The names within an object SHOULD be unique.
object = begin-object [ member *( value-separator member ) ]
end-object
An object whose names are all unique is interoperable in the sense
that all software implementations receiving that object will agree on
the name-value mappings. When the names within an object are not
unique, the behavior of software that receives such an object is
unpredictable.
Examples (from page 12 of RFC)
This is a JSON object:
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http://www.example.com/image/481989943",
"Height": 125,
"Width": 100
},
"Animated" : false,
"IDs": [116, 943, 234, 38793]
}
}
Its Image member is an object whose Thumbnail member is an object and
whose IDs member is an array of numbers.
There is really no such thing as a "JSON Object".
Really?
First you should know what JSON is:
It is language agnostic data-interchange format.
The syntax of JSON was inspired by the JavaScript Object Literal notation, but there are differences between them.
For example, in JSON all keys must be quoted, while in object literals this is not necessary:
// JSON:
{ "foo": "bar" }
// Object literal:
var o = { foo: "bar" };
The quotes are mandatory on JSON because in JavaScript (more exactly in ECMAScript 3rd. Edition), the usage of reserved words as property names is disallowed, for example:
var o = { if: "foo" }; // SyntaxError in ES3
While, using a string literal as a property name (quoting the property name) gives no problems:
var o = { "if": "foo" };
So for "compatibility" (and easy eval'ing maybe?) the quotes are mandatory.
The data types in JSON are also restricted to the following values:
string
number
object
array
A literal as:
true
false
null
The grammar of Strings changes. They have to be delimited by double quotes, while in JavaScript, you can use single or double quotes interchangeably.
// Invalid JSON:
{ "foo": 'bar' }
The accepted JSON grammar of Numbers also changes, in JavaScript you can use Hexadecimal Literals, for example 0xFF, or (the infamous) Octal Literals e.g. 010. In JSON you can use only Decimal Literals.
// Invalid JSON:
{ "foo": 0xFF }
Here is one surprising difference: you can not use undefined in json and all object fields with undefined values will disappear after JSON.stringify
let object = { "a": undefined } ;
let badJSON= '{ "a": undefined }';
console.log('valid JS object :', object );
console.log('JSON from object:', JSON.stringify(object) );
console.log('invalid json :', JSON.parse(badJSON) );
🙈🙉🙊
Javascript Object Literal vs JSON:
Object literal syntax is a very convenient way to create javascript objects
The JSON language, which stands for 'Javascript object notation', has its syntax derived from javascript object literal syntax. It is used as a programming language independent textual data transfer format.
Example:
JS object notation, used in JS to create objects in the code conveniently:
const JS_Object = {
1: 2, // the key here is the number 1, the value is the number 2
a: 'b', // the key is the string a, the value is the string b
func: function () { console.log('hi') }
// the key is func, the value is the function
}
Example of JSON:
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}
Main differences:
All object keys in JSON must be strings. In Javascript object keys can be strings or numbers
All strings in JSON must be quoted in "double quotes". Whereas in Javascript both single quotes and double quotes are allowed. Even with no quotes in the Javascript object notation the object keys are implicitly casted to strings.
In JSON a function cannot be defined as a value of an object (since this is Javascript specific). In Javascript this is completely legal.
Javascript build in JSON object:
JSON objects can be easily converted to Javascript and vice versa using the built in JSON object which Javascript offers in its runtime. For example:
const Object = {
property1: true,
property2: false,
}; // creating object with JS object literal syntax
const JSON_object = JSON.stringify(Object); // stringify JS object to a JSON string
console.log(JSON_object); // note that the (string) keys are in double quotes
const JS_object = JSON.parse(JSON_object); // parse JSON string to JS object
console.log(JS_object.property1, JS_object.property2);
// accessing keys of the newly created object

Difference between toJSON() and JSON.Stringify()

if you need to read or clone all of a model’s data attributes, use its
toJSON() method. This method returns a copy of the attributes as an
object (not a JSON string despite its name). (When JSON.stringify() is
passed an object with a toJSON() method, it stringifies the return
value of toJSON() instead of the original object. The examples in the
previous section took advantage of this feature when they called
JSON.stringify() to log model instances.)
http://addyosmani.github.io/backbone-fundamentals/#backbone-basics
Can anyone tell me the difference between both these ways of representing an object in JSON notation. I am just confused whether these to achieve the same or there is a difference.
From the fine manual:
toJSON behavior
If an object being stringified has a property named toJSON whose value is a function, then the toJSON method customizes JSON stringification behavior: instead of the object being serialized, the value returned by the toJSON method when called will be serialized.
This is why Backbone uses the toJSON method for serialization and given a model instance called m, you can say things like:
var string = JSON.stringify(m);
and get just the attributes out of m rather than a bunch of noise that your server won't care about.
That said, the main difference is that toJSON produces a value (a number, boolean, object, ...) that gets converted to a JSON string whereas JSON.stringify always produces a string.
The default Backbone toJSON is simply this (for models):
return _.clone(this.attributes);
so m.toJSON() gives you a shallow copy of the model's attributes. If there are arrays or objects as attribute values then you will end unexpected reference sharing. Note that Backbone.Model#clone also suffers from this problem.
If you want to safely clone a model's data then you could send it through JSON.stringify and then JSON.parse to get a deep copy:
var data = JSON.parse(JSON.stringify(model_instance));
var cloned_model = new M(data);
where model_instance is your instance of the Backbone model M.
JSON.stringify() - Any valid JSON representation value can be stringified.
The JSON.stringify(..) utility will automatically omit undefined, function, and symbol values when it comes across them. If such a value is found in an array, that value is replaced by null (so that the array position information isn't altered). If found as a property of an object, that property will simply be excluded.
JSON stringification has the special behavior that if an object value has a toJSON() method defined, this method will be called first to get a value to use for serialization.
toJSON() - to a valid JSON value suitable for stringification.
One example, JSON.stringify() an object with circular reference in it, an error will be thrown. toJSON() can fix it as following.
var o = { };
var a = {
b: 32,
c: o
};
// circular reference
o.d = a;
// JSON.stringify( a ); // an error caused by circular reference
// define toJSON method
a.toJSON = function() {
return { b: this.b };
};
JSON.stringify( a ); // "{"b":32}"
I'm also reading Addy Osmani's Developing backbone.js application, and I have the same question. I figured out by trying his example (the todo list) in the console.
var Todo = Backbone.Model.extend({
defaults:{
title:"",
completed:false
}
});
var todo1 = new Todo();
console.log(todo1.toJSON())
//The console shows
//Object {title: "finish your assignment", completed: false}
console.log(JSON.stringify(todo1))
//The console shows
//{"title":"finish your assignment","completed":false}

Cast vs ToXXX for value handles in v8

I'm embedding V8 as an auxiliary language in a C++ program.
I retrieve a Handle<Value> from V8 when I call something like
Handle<Value> value_handle = context->Global()->Get(key_handle);
I can then find out that it is (say) a string with value_handle->IsString(). And if so I can convert it to a Handle<String> to access its string-specific methods.
But there seems to be two ways of doing that, either:
Handle<String> string = value_handle->ToString();
or
Handle<String> string = Handle<String>::Cast(value_handle);
However, for Arrays and Functions, there is no toArray() or toFunction methods, just the casting.
So my question is:
a) are the ToXXX just syntactic sugar for casting?
and, if not b) what is the ToXXX method doing?
ToXXX functions perform type coercions as described in subsections of section 9 of ECMA-262 5th. For example ToString is described in section 9.8: when given a non-string value it'll return an appropriate string representation of it, if you are passing object it'll call toString method on it (or valueOf if toString is not present). Relevant code for ToString: in api.cc Value::ToString that calls into runtime.js ToString
On the other hand Handle<XXX>::Cast(...) does no coercions. It's just a type cast for handles. Essentially it is just a static_cast<XXX*>. In debug mode Handle<T>::Cast(...) is checked and aborts execution when types do not match. It would be a fatal error if you are given a Handle<Value> containing an Object and you are trying to cast it to a Handle<String>. In release mode casting to an incompatible type will just later lead to weird results and possibly crashes, when you try to use the result of the cast. Relevant code in v8.h Handle<T>::Cast which delegates to (for example) String::Cast which checks the cast (if checks are enabled) via String::CheckCast.
We can locate
V8EXPORT Local ToString() const;
in line 971 of v8.h
where V8EXPORT is a OS dependent approach for functions.
ToString of Handle of String is located at line 2362 of api.cc
Local<String> Value::ToString() const {
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::Object> str;
if (obj->IsString()) {
str = obj;
} else {
i::Isolate* isolate = i::Isolate::Current();
if (IsDeadCheck(isolate, "v8::Value::ToString()")) {
return Local<String>();
}
LOG_API(isolate, "ToString");
ENTER_V8(isolate);
EXCEPTION_PREAMBLE(isolate);
str = i::Execution::ToString(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(isolate, Local<String>());
}
return Local<String>(ToApi<String>(str));
}
For consistency and take advantages from further upgrade of V8 versions, I strongly recommend to use toString() instead of the primitive cast.

Primitives and Objects

Definitive JavaScript by David Flanagan makes a distinction between Objects and Primitives.
He defines the primitives as Number, String, Boolean, Null, and Undefined, as does the standard.
However, would it be more accurate to define a primitive, as subset of object, i.e. to call them Primitive Objects.
Because they have their own methods and are complex entities.
Actual Question
Would Primitive Object be more accurate than Object when defining String, Boolean, and Number?
Objects and primitives are distinct:
typeof 42 === "number"
typeof new Number(42) === "object"
new Number(42) !== 42
However, when necessary, primitives are automatically wrapped by temporary objects, which can be automatically converted back into primitives:
(42).toString() === "42"
new Number(42) == 42
new Number(42) + 1 === 43
Especially in the context of the Java and C# programming languages, this sort of behavior is called autoboxing. As wrapper objects have some confusing characteristics, for example:
Boolean(new Boolean(false)) === true
it is good practice to avoid intentionally storing them in variables and instead use primitives whenever possible.
It's not about semantics, look:
var threePrimitive = 3;
var threeObject = new Number(3);
threePrimitive.toFixed(2); // 3.00
threeObject.toFixed(2); // 3.00
threePrimitive.foo = true
threeObject.foo = true;
threePrimitive.foo; // undefined
threeObject.foo; // true
Primitives are wrapped in objects when you try to call a method on them, but after initial use the object is thrown away.
As for how this is stated in the specification, I'm not 100% sure, but here is what I think (based on the tips left by Bergi in one of his answers. Section 11.2.1 states that the accessor properties should be evaluated as follows:
Let baseReference be the result of evaluating MemberExpression.
Let baseValue be GetValue(baseReference).
(...)
Then in 8.7.1 we see the following:
The following [[Get]] internal method is used by GetValue when V is a
property reference with a primitive base value. It is called using
base as its this value and with property P as its argument. The
following steps are taken:
Let O be ToObject(base).
Let desc be the result of calling the
[[GetProperty]] internal method of O with property name P.
If desc is
undefined, return undefined.
If IsDataDescriptor(desc) is true, return
desc.[[Value]].
Otherwise, IsAccessorDescriptor(desc) must be true so,
let getter be desc.[[Get]]. If getter is undefined, return undefined.
Return the result calling the [[Call]] internal method of getter
providing base as the this value and providing no arguments.
NOTE The
object that may be created in step 1 is not accessible outside of the
above method. An implementation might choose to avoid the actual
creation of the object. The only situation where such an actual
property access that uses this internal method can have visible effect
is when it invokes an accessor function.
Would Primitive Object be more accurate then Object when defining
String, Boolean, and Number?
Please note that I'm not saying that numbers are not objects here, I'm pointing out that it appears ambiguous. This is the kind of thing that confuses JavaScript newcomers.
The distinction is mostly academic, but there is one case where it seems ambiguous: literals represent primitive objects except when the literal appears to represent a number. You can't apply a method directly to a literal integer* symbol:
1.toString();
SyntaxError: identifier starts immediately after numeric literal
…but you can apply methods of Numbers:
Number(1).toString();
'1'
…and a name that contains a number is a Number:
x = 4;
x.toString();
'4'
I think this is actually a parsing problem, but I don't really know why the parser can't tell that 1 is a Number as easily as it can tell that "abc" is a String. I suppose it has to do with the semantic ambiguity of the . symbol. (Is it a decimal point or a method operator?)
*JavaScript doesn't actually have integers. I just mean a symbol that consists entirely of [0-9]+.

How to parse or query complex JSON in JavaScript

Is it possible to perform complex queries over a JSON object? I am open to JavaScript or jQuery solutions, the easier the better. I'm envisioning some kind of functional programming language similar to LINQ or SQL.
I Prefer no other third party libraries or add-ons.
UPDATE
From the looks of early answers, an add-on is going to be necessary. In that case, I prefer an add-on that requires no installation process. Something that deploys with the software publish (like jQuery) is fine (e.g. sets of *.js files).
Check out: Is there a query language for JSON?
From that thread:
JaQL(Wiki)
JsonPath.
Json Query
By the time you're interacting with it, it's not a "JSON object," it's a JavaScript object. ("JSON objects" only exist in terms of the data notation.) JavaScript itself doesn't have any advanced functional programming constructs, so you'd need third party libraries for that sort of thing. JavaScript pretty much just has property accessors, an operator for "does this object have a property with this name?" (in, hasOwnProperty), and as of the 5th edition (not yet widely supported), some handy array-specific features like forEach, every, map, filter, and the like.
Use JSON.stringify and a replacer callback to achieve this:
function replacer(match, offset, fullstring)
{
return replacer.str;
}
replacer.str = "\u0022filterValues\u0022:[\u0022hi\u0022,\u0022bye\u0022]"; /* Use DOM node value */
var foo = JSON.stringify({
"Region": {
"filterField": "kw_Region",
"filterValues": [
"aa",
"bb"
]
},
"ApplicationName": {
"filterField": "kw_ApplicationName",
"filterValues": [
"aa",
"bb"
]
},
"IssueType": {
"filterField": "kw_IssueType",
"filterValues": [
"aa",
"bb"
]
},
"Outage": {
"filterField": "kw_Outage",
"filterValues": [
"aa",
"bb"
]
},
"Priority": {
"filterField": "kw_Priority",
"filterValues": [
"aa",
"bb"
]
}
}).replace(/"filterValues[^\]]+./g, replacer)
Here is some documentation on the two JSON methods for serialization and transformation, stringify and parse:
JSON.parse(source, reviver)
This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted.
The reviver is ultimately called with the empty string and the topmost value to permit transformation of the topmost value. Be certain to handle this case properly, usually by returning the provided value, or JSON.parse will return undefined.
if (k === "") return v
JSON.stringify(value, replacer, space)
The stringify method produces a JSON text from a JavaScript value. If value is an object or array, the structure will be visited recursively to determine the serialization of each membr or element. The structure must not be cyclical.
When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the object holding the key.
You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization.
If the replacer parameter is an array, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified.
Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read.
If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces.
var alias = {"Clark":"","phone":""};
function kryptonite(key)
{
var replacement = {};
for(var cursor in this)
{
if(cursor in alias)
replacement[cursor] = this[cursor]
}
return replacement;
}
var contact = {
"Clark":"Kent",
"Kal El":"Superman",
"phone":"555-7777"
}
contact.toJSON = kryptonite;
var foo = JSON.stringify(contact) // "{"Clark":"Kent","phone":"555-7777"}"
References
ECMAScript Wiki:JSON Support
MDN:JSON.parse Examples
MDN:JSON.stringify toJSON behavior
MSDN:toJSON Method
Opera:JSON.parse
hmm... YQL does this sort of thing, but that would be a third party.
you could filter an array with the jQuery $.grep(array,filterfn) method
var newArr = $.grep(oldArr,function(elInArray,index){
return elInArray.key === somevalue;
});
and you can of course use a regexp in there if you wish, or use more complex conditions, such as checking multiple keys, keys within keys, arrays within keys, etc.

Categories

Resources