How to stringify inherited objects to JSON? - javascript

json2.js seems to ignore members of the parent object when using JSON.stringify(). Example:
require('./json2.js');
function WorldObject(type) {
this.position = 4;
}
function Actor(val) {
this.someVal = 50;
}
Actor.prototype = new WorldObject();
var a = new Actor(2);
console.log(a.position);
console.log(JSON.stringify(a));
The output is:
4
{"someVal":50}
I would expect this output:
4
{"position":0, "someVal":50}

Well that's just the way it is, JSON.stringify does not preserve any of the not-owned properties of the object. You can have a look at an interesting discussion about other drawbacks and possible workarounds here.
Also note that the author has not only documented the problems, but also written a library called HydrateJS that might help you.
The problem is a little bit deeper than it seems at the first sight. Even if a would really stringify to {"position":0, "someVal":50}, then parsing it later would create an object that has the desired properties, but is neither an instance of Actor, nor has it a prototype link to the WorldObject (after all, the parse method doesn't have this info, so it can't possibly restore it that way).
To preserve the prototype chain, clever tricks are necessary (like those used in HydrateJS). If this is not what you are aiming for, maybe you just need to "flatten" the object before stringifying it. To do that, you could e.g. iterate all the properties of the object, regardless of whether they are own or not and re-assign them (this will ensure they get defined on the object itself instead of just inherited from the prototype).
function flatten(obj) {
var result = Object.create(obj);
for(var key in result) {
result[key] = result[key];
}
return result;
}
The way the function is written it doesn't mutate the original object. So using
console.log(JSON.stringify(flatten(a)));
you'll get the output you want and a will stay the same.

Another option would be to define a toJSON method in the object prototype you want to serialize:
function Test(){}
Test.prototype = {
someProperty: "some value",
toJSON: function() {
var tmp = {};
for(var key in this) {
if(typeof this[key] !== 'function')
tmp[key] = this[key];
}
return tmp;
}
};
var t = new Test;
JSON.stringify(t); // returns "{"someProperty" : "some value"}"
This works since JSON.stringify searches for a toJSON method in the object it receives, before trying the native serialization.

Check this fiddle: http://jsfiddle.net/AEGYG/
You can flat-stringify the object using this function:
function flatStringify(x) {
for(var i in x) {
if(!x.hasOwnProperty(i)) {
// weird as it might seem, this actually does the trick! - adds parent property to self
x[i] = x[i];
}
}
return JSON.stringify(x);
}

Here is a recursive version of the snippet #TomasVana included in his answer, in case there is inheritance in multiple levels of your object tree:
var flatten = function(obj) {
if (obj === null) {
return null;
}
if (Array.isArray(obj)) {
var newObj = [];
for (var i = 0; i < obj.length; i++) {
if (typeof obj[i] === 'object') {
newObj.push(flatten(obj[i]));
}
else {
newObj.push(obj[i]);
}
}
return newObj;
}
var result = Object.create(obj);
for(var key in result) {
if (typeof result[key] === 'object') {
result[key] = flatten(result[key]);
}
else {
result[key] = result[key];
}
}
return result;
}
And it keeps arrays as arrays. Call it the same way:
console.log(JSON.stringify(flatten(visualDataViews)));

While the flatten approach in general works, the snippets in other answers posted so far don't work for properties that are not modifiable, for example if the prototype has been frozen. To handle this case, you would need to create a new object and assign the properties to this new object. Since you're just stringifying the resulting object, object identity and other JavaScript internals probably don't matter, so it's perfectly fine to return a new object. This approach is also arguably more readable than reassigning an object's properties to itself, since it doesn't look like a no-op:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}

JSON.stringify takes three options
JSON.stringify(value[, replacer[, space]])
So, make use of the replacer, which is a function, that is called recursively for every key-value-pair.
Next Problem, to get really everything, you need to follow the prototpes and you must use getOwnPropertyNames to get all property names (more than you can catch with keysor for…in):
var getAllPropertyNames = () => {
const seen = new WeakSet();
return (obj) => {
let props = [];
do {
if (seen.has(obj)) return [];
seen.add(obj);
Object.getOwnPropertyNames(obj).forEach((prop) => {
if (props.indexOf(prop) === -1) props.push(prop);
});
} while ((obj = Object.getPrototypeOf(obj)));
return props;
};
};
var flatten = () => {
const seen = new WeakSet();
const getPropertyNames = getAllPropertyNames();
return (key, value) => {
if (value !== null && typeof value === "object") {
if (seen.has(value)) return;
seen.add(value);
let result = {};
getPropertyNames(value).forEach((k) => (result[k] = value[k]));
return result;
}
return value;
};
};
Then flatten the object to JSON:
JSON.stringify(myValue, flatten());
Notes:
I had a case where value was null, but typeof value was "object"
Circular references must bee detected, so it needs seen

Related

Create object with sub-objects up to the n-th level [duplicate]

This question already has answers here:
How to set object property (of object property of..) given its string name in JavaScript?
(16 answers)
Closed 6 years ago.
I have an object called MyObject and a method called createEntry() which aims to create a new property for MyObject and sub-properties for the property or just skip it altogether if it already exists.
My code:
var MyObject = {
createEntry: function (val1, val2, val3, val4) {
this[val1] = this[val1] || {};
this[val1][val2] = this[val1][val2] || {};
this[val1][val2][val3] = val4;
}
};
MyObject.createEntry("val1", "val2", "val3", "val4");
As shown in the above function, I'm trying to create a new sub-object for each argument of the method createEntry() except for the last two, where val3 is a property or method and val4 is its value.
With my method in its current state, I can only reach up to level 3 with its subsequent ones requiring longer and longer code. I assume the above can be achieved with a while loop, but I haven't been able to figure it out yet.
Question: How can I create unlimited sub-objects based on the number of arguments passed on the above function in a tree fashion that looks like the following:
var MyObject = {
val1: {
val2 {
val3: val4
}
}
}
reduceRight seems perfect for this:
function createEntry(...args) {
return args.reduceRight(function(prev, curr) {
return {[curr]: prev};
});
}
console.log(createEntry("val1", "val2", "val3", "val4"));
Well, when creating unlimited sub-objects using parameters, I'd iterate each parameter, having the last object I entered as reference. This code is enough to understand.
Note: Object.assign is a new browser implementation, but it has polyfills already. I use this method to concatenate object with object when a entry name already exists in a object
Object['prototype']['createEntries'] = function() {
/* store last object location */
var lastObj = this
/**
* Note: each argument is a object that specify the following properties -->
* inner : the entry object
* *name : the entry property name
*/
for (var i = 0, arg, existent, len = arguments.length; i < len; ++i) {
if (typeof (arg = arguments[i]) === 'object' && (typeof arg.inner === 'object' ? true : arg.inner = {} ))
/* create new entry/keep existent entry and reserve it in lastObj */
lastObj = (typeof (existent = lastObj[arg.name]) === 'object' ?
lastObj[arg.name] = Object.assign(existent, arg.inner) :
lastObj[arg.name] = arg.inner)
}
return this
}
Then this is a basic usage
({}).createEntries({
name: "val1"
}, {
name: "val2"
})
/* {
val1: {
val2: {
}
}
}
*/
I hope this is what you want
Oriol gave a fantastic answer, but his function, due to the way it's built, is not really useful as it returns a new object instead of modifying MyObject.
So, in order to actually modify MyObject, instead of the first return, we have to do something else that will modify MyObject without overwriting it.
To do just that, a function that expands the MyObject must be built:
expand: function (object) {
for (var key in object) if (!!object[key]) {
if (key in this) throw new Error("`" + key + "` already exists.");
else this[key] = object[key];
}
}
Then, all we have to do is to use #Oriol's answer without the first return statement as an argument to expand():
createEntry: function () {
this.expand([].reduceRight.call(arguments, function(previous, current) {
return {[current]: previous};
}));
}
Full code:
var MyObject = {
createEntry: function() {
this.expand([].reduceRight.call(arguments, function(previous, current) {
return {
[current]: previous
};
}));
},
expand: function(object) {
for (var key in object)
if (!!object[key]) {
if (key in this) throw new Error("`" + key + "` already exists.");
else this[key] = object[key];
}
}
};
MyObject.createEntry("val1", "val2", "val3", "val4");
console.log(MyObject);
Thanks to everyone for the help!

List All Prototype Properties of a Javascript Object

Is there any other way to look up for the prototype properties of an javascript object. Lets say I have like this.
function proton() {
this.property1 = undefined;
this.property2 = undefined;
};
proton.prototype = {
sample1 : function() {
return 'something';
},
sample2 : function() {
return 'something';
}
};
var my_object = new proton();
console.log(Object.keys(my_object));
returns ["property1", "property2"]
console.log(Object.getOwnPropertyNames(my_object));
returns ["property1", "property2"]
But what i want to print is the prototype properties of the object my_object.
['sample1', 'sample2']
In order for me to see the prototype properties of that object i need to console.log(object) and from developer tools i can look up for the properties of that object.
But since I am using third party libraries like phaser.js, react.js, create.js
so i don't know the list of the prototype properties of a created object from this libraries.
Is there any prototype function of Object to list down all the prototpye properties of a javascript object?
Not a prototype method, but you can use Object.getPrototypeOf to traverse the prototype chain and then get the own property names of each of those objects.
function logAllProperties(obj) {
if (obj == null) return; // recursive approach
console.log(Object.getOwnPropertyNames(obj));
logAllProperties(Object.getPrototypeOf(obj));
}
logAllProperties(my_object);
Using this, you can also write a function that returns you an array of all the property names:
function props(obj) {
var p = [];
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
var op = Object.getOwnPropertyNames(obj);
for (var i=0; i<op.length; i++)
if (p.indexOf(op[i]) == -1)
p.push(op[i]);
}
return p;
}
console.log(props(my_object)); // ["property1", "property2", "sample1", "sample2", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"
function prototypeProperties(obj) {
var result = [];
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
}
EDIT: This will grab all the properties that were defined on any ancestor. If you want a more granular control of what is defined where, Bergi's suggestion is good.
The quick and dirty one-liner solution would be:
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf({ prop1: 'val1' })))
If you want something more precise, go with the accepted answer!

managing object construction parameters in javascript

I am posting this in hopes that someone might have dealt with a similar problem.
I am using a javascript object that encapsulates paramaters to intialize greater objects in my code, like so :
function MyObject(setup)
{
this.mysetup = setup;
if(typeof this.mysetup == "undefined") { this.mysetup = {} }
if(typeof this.mysetup.stringParameter == "undefined")
{
this.mysetup.stringParameter="string default value"
}
if(typeof this.mysetup.objParameter == "undefined")
{
this.mysetup.objParameter == {}
}
else
{
if(typeof this.mysetup.objParameter.member1 == "undefined")
{
this.mysetup.objParameter.member1 = "member1 default value"
}
}
// ...and so on
}
This way I can make sure not every parameter needs to be in setup, and still MyObject can resort to default values for what is missing.
However, this is a tedious thing to write and quite error prone. So I thought I'd try for a solution that checks the setup against a setupPrototype:
function MyObject(setup)
{
this.setupPrototype = {
stringParameter : "string default value",
objectParameter : { member1 : "member default value"}
}
}
and try to compare the setup against this.setupPrototype.
The function I'm putting together for this purpose looks like
parseSetup = function (obj, objPrototype)
{
var returnedObj = {};
var hasMembers = false;
if(typeof obj=="undefined")
{
returnedObj = objPrototype;
return returnedObj;
}
for(member in objPrototype)
{
hasMembers = true;
//if prototype member is not part of initialization object
if (typeof obj[member]=="undefined")
{
returnedObj[member] = objPrototype[member];
}
else
{
if(objPrototype[member] instanceof Object)
{
if(objPrototype[member] instanceof Array)
{
returnedObj[member]=[];
for(var i=0; i<objPrototype[member].length; i++)
{
returnedObj[member].push(parseSetup(obj[member][i], objPrototype[member][i]))
}
}
else{
returnedObj[member] = parseSetup(obj[member], objPrototype[member])
}
}
else
returnedObj[member] = obj[member];
}
}
if(!hasMembers)
{
if (typeof obj == "undefined")
{
returnedObj = objPrototype;
}
else
returnedObj = obj;
}
return returnedObj;
}
This however is still not up to par.
An additional issue, which I'm debating is whether the original 'setup' should retain any of its own initial properties, or just have whatever is in the prototype. Also, it would be pointless to require that the prototype itself be aware of every possible value the setup might contain, especially for deep nested objects.
So my question is, are you aware of any proper way to solve this problem and end up with a setup object that, where its parameters are missing, can get default values from the prototype, but also not lose its own where they somehow need to be kept?
Many thanks
I would recommend using jQuery and then taking advantage of the $.extend() function, as described on the jQuery plugins page. Basically, you define your default parameters as an object within the constructor method, and then use $.extend() to overwrite only the properties that are supplied in the parameter to your function.
So you might end up with something like:
var MyObject = function (options) {
this.defaultOptions = {
stringParameter: "string default value",
objParameter: {}
};
this.options = $.extend(true, this.defaultOptions, options);
};
To instantiate with the default parameters:
var obj1 = new MyObject({});
To instantiate with an overridden stringParameter:
var obj2 = new MyObject({stringParameter: 'overridden value'});
You can see a demo of this in action here.

Parse JSON String into a Particular Object Prototype in JavaScript

I know how to parse a JSON String and turn it into a JavaScript Object.
You can use JSON.parse() in modern browsers (and IE9+).
That's great, but how can I take that JavaScript Object and turn it into a particular JavaScript Object (i.e. with a certain prototype)?
For example, suppose you have:
function Foo()
{
this.a = 3;
this.b = 2;
this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse({"a":4, "b": 3});
//Something to convert fooJSON into a Foo Object
//....... (this is what I am missing)
alert(fooJSON.test() ); //Prints 12
Again, I am not wondering how to convert a JSON string into a generic JavaScript Object. I want to know how to convert a JSON string into a "Foo" Object. That is, my Object should now have a function 'test' and properties 'a' and 'b'.
UPDATE
After doing some research, I thought of this...
Object.cast = function cast(rawObj, constructor)
{
var obj = new constructor();
for(var i in rawObj)
obj[i] = rawObj[i];
return obj;
}
var fooJSON = Object.cast({"a":4, "b": 3}, Foo);
Will that work?
UPDATE May, 2017: The "modern" way of doing this, is via Object.assign, but this function is not available in IE 11 or older Android browsers.
The current answers contain a lot of hand-rolled or library code. This is not necessary.
Use JSON.parse('{"a":1}') to create a plain object.
Use one of the standardized functions to set the prototype:
Object.assign(new Foo, { a: 1 })
Object.setPrototypeOf({ a: 1 }, Foo.prototype)
See an example below (this example uses the native JSON object). My changes are commented in CAPITALS:
function Foo(obj) // CONSTRUCTOR CAN BE OVERLOADED WITH AN OBJECT
{
this.a = 3;
this.b = 2;
this.test = function() {return this.a*this.b;};
// IF AN OBJECT WAS PASSED THEN INITIALISE PROPERTIES FROM THAT OBJECT
for (var prop in obj) this[prop] = obj[prop];
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
// INITIALISE A NEW FOO AND PASS THE PARSED JSON OBJECT TO IT
var fooJSON = new Foo(JSON.parse('{"a":4,"b":3}'));
alert(fooJSON.test() ); //Prints 12
Do you want to add JSON serialization/deserialization functionality, right? Then look at this:
You want to achieve this:
toJson() is a normal method.
fromJson() is a static method.
Implementation:
var Book = function (title, author, isbn, price, stock){
this.title = title;
this.author = author;
this.isbn = isbn;
this.price = price;
this.stock = stock;
this.toJson = function (){
return ("{" +
"\"title\":\"" + this.title + "\"," +
"\"author\":\"" + this.author + "\"," +
"\"isbn\":\"" + this.isbn + "\"," +
"\"price\":" + this.price + "," +
"\"stock\":" + this.stock +
"}");
};
};
Book.fromJson = function (json){
var obj = JSON.parse (json);
return new Book (obj.title, obj.author, obj.isbn, obj.price, obj.stock);
};
Usage:
var book = new Book ("t", "a", "i", 10, 10);
var json = book.toJson ();
alert (json); //prints: {"title":"t","author":"a","isbn":"i","price":10,"stock":10}
var book = Book.fromJson (json);
alert (book.title); //prints: t
Note: If you want you can change all property definitions like this.title, this.author, etc by var title, var author, etc. and add getters to them to accomplish the UML definition.
A blog post that I found useful:
Understanding JavaScript Prototypes
You can mess with the __proto__ property of the Object.
var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;
This allows fooJSON to inherit the Foo prototype.
I don't think this works in IE, though... at least from what I've read.
Am I missing something in the question or why else nobody mentioned reviver parameter of JSON.parse since 2011?
Here is simplistic code for solution that works:
https://jsfiddle.net/Ldr2utrr/
function Foo()
{
this.a = 3;
this.b = 2;
this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse(`{"a":4, "b": 3}`, function(key,value){
if(key!=="") return value; //logic of course should be more complex for handling nested objects etc.
let res = new Foo();
res.a = value.a;
res.b = value.b;
return res;
});
// Here you already get Foo object back
alert(fooJSON.test() ); //Prints 12
PS: Your question is confusing: >>That's great, but how can I take that JavaScript Object and turn it into a particular JavaScript Object (i.e. with a certain prototype)?
contradicts to the title, where you ask about JSON parsing, but the quoted paragraph asks about JS runtime object prototype replacement.
The currently accepted answer wasn't working for me. You need to use Object.assign() properly:
class Person {
constructor(name, age){
this.name = name;
this.age = age;
}
greet(){
return `hello my name is ${ this.name } and i am ${ this.age } years old`;
}
}
You create objects of this class normally:
let matt = new Person('matt', 12);
console.log(matt.greet()); // prints "hello my name is matt and i am 12 years old"
If you have a json string you need to parse into the Person class, do it like so:
let str = '{"name": "john", "age": 15}';
let john = JSON.parse(str); // parses string into normal Object type
console.log(john.greet()); // error!!
john = Object.assign(Person.prototype, john); // now john is a Person type
console.log(john.greet()); // now this works
An alternate approach could be using Object.create. As first argument, you pass the prototype, and for the second one you pass a map of property names to descriptors:
function SomeConstructor() {
};
SomeConstructor.prototype = {
doStuff: function() {
console.log("Some stuff");
}
};
var jsonText = '{ "text": "hello wrold" }';
var deserialized = JSON.parse(jsonText);
// This will build a property to descriptor map
// required for #2 argument of Object.create
var descriptors = Object.keys(deserialized)
.reduce(function(result, property) {
result[property] = Object.getOwnPropertyDescriptor(deserialized, property);
}, {});
var obj = Object.create(SomeConstructor.prototype, descriptors);
I like adding an optional argument to the constructor and calling Object.assign(this, obj), then handling any properties that are objects or arrays of objects themselves:
constructor(obj) {
if (obj != null) {
Object.assign(this, obj);
if (this.ingredients != null) {
this.ingredients = this.ingredients.map(x => new Ingredient(x));
}
}
}
For the sake of completeness, here's a simple one-liner I ended up with (I had no need checking for non-Foo-properties):
var Foo = function(){ this.bar = 1; };
// angular version
var foo = angular.extend(new Foo(), angular.fromJson('{ "bar" : 2 }'));
// jquery version
var foo = jQuery.extend(new Foo(), jQuery.parseJSON('{ "bar" : 3 }'));
I created a package called json-dry. It supports (circular) references and also class instances.
You have to define 2 new methods in your class (toDry on the prototype and unDry as a static method), register the class (Dry.registerClass), and off you go.
While, this is not technically what you want, if you know before hand the type of object you want to handle you can use the call/apply methods of the prototype of your known object.
you can change this
alert(fooJSON.test() ); //Prints 12
to this
alert(Foo.prototype.test.call(fooJSON); //Prints 12
I've combined the solutions that I was able to find and compiled it into a generic one that can automatically parse a custom object and all it's fields recursively so you can use prototype methods after deserialization.
One assumption is that you defined a special filed that indicates it's type in every object you want to apply it's type automatically (this.__type in the example).
function Msg(data) {
//... your init code
this.data = data //can be another object or an array of objects of custom types.
//If those objects defines `this.__type', their types will be assigned automatically as well
this.__type = "Msg"; // <- store the object's type to assign it automatically
}
Msg.prototype = {
createErrorMsg: function(errorMsg){
return new Msg(0, null, errorMsg)
},
isSuccess: function(){
return this.errorMsg == null;
}
}
usage:
var responseMsg = //json string of Msg object received;
responseMsg = assignType(responseMsg);
if(responseMsg.isSuccess()){ // isSuccess() is now available
//furhter logic
//...
}
Type assignment function (it work recursively to assign types to any nested objects; it also iterates through arrays to find any suitable objects):
function assignType(object){
if(object && typeof(object) === 'object' && window[object.__type]) {
object = assignTypeRecursion(object.__type, object);
}
return object;
}
function assignTypeRecursion(type, object){
for (var key in object) {
if (object.hasOwnProperty(key)) {
var obj = object[key];
if(Array.isArray(obj)){
for(var i = 0; i < obj.length; ++i){
var arrItem = obj[i];
if(arrItem && typeof(arrItem) === 'object' && window[arrItem.__type]) {
obj[i] = assignTypeRecursion(arrItem.__type, arrItem);
}
}
} else if(obj && typeof(obj) === 'object' && window[obj.__type]) {
object[key] = assignTypeRecursion(obj.__type, obj);
}
}
}
return Object.assign(new window[type](), object);
}
A very simple way to get the desired effect is to add an type attribute while generating the json string, and use this string while parsing the string to generate the object:
serialize = function(pObject) {
return JSON.stringify(pObject, (key, value) => {
if (typeof(value) == "object") {
value._type = value.constructor.name;
}
return value;
});
}
deSerialize = function(pJsonString) {
return JSON.parse(pJsonString, (key, value) => {
if (typeof(value) == "object" && value._type) {
value = Object.assign(eval('new ' + value._type + '()'), value);
delete value._type;
}
return value;
});
}
Here a little example of use:
class TextBuffer {
constructor() {
this.text = "";
}
getText = function() {
return this.text;
}
setText = function(pText) {
this.text = pText;
}
}
let textBuffer = new TextBuffer();
textBuffer.setText("Hallo");
console.log(textBuffer.getText()); // "Hallo"
let newTextBuffer = deSerialize(serialize(textBuffer));
console.log(newTextBuffer.getText()); // "Hallo"
Here is a solution using typescript and decorators.
Objects keep their methods after deserialization
Empty objects and their children are default-initialized
How to use it:
#SerializableClass
class SomeClass {
serializedPrimitive: string;
#SerializableProp(OtherSerializedClass)
complexSerialized = new OtherSerializedClass();
}
#SerializableClass
class OtherSerializedClass {
anotherPrimitive: number;
someFunction(): void {
}
}
const obj = new SomeClass();
const json = Serializable.serializeObject(obj);
let deserialized = new SomeClass();
Serializable.deserializeObject(deserialized, JSON.parse(json));
deserialized.complexSerialized.someFunction(); // this works!
How it works
Serialization:
Store the type name in the prototype (__typeName)
Use JSON.stringify with a replacer method that adds __typeName to the JSON.
Deserialization:
Store all serializable types in Serializable.__serializableObjects
Store a list of complex typed properties in every object (__serializedProps)
Initialize an object theObject via the type name and __serializableObjects.
Go through theObject.__serializedProps and traverse over it recursively (start at last step with every serialized property). Assign the results to the according property.
Use Object.assign to assign all remaining primitive properties.
The code:
// #Class decorator for serializable objects
export function SerializableClass(targetClass): void {
targetClass.prototype.__typeName = targetClass.name;
Serializable.__serializableObjects[targetClass.name] = targetClass;
}
// #Property decorator for serializable properties
export function SerializableProp(objectType: any) {
return (target: {} | any, name?: PropertyKey): any => {
if (!target.constructor.prototype?.__serializedProps)
target.constructor.prototype.__serializedProps = {};
target.constructor.prototype.__serializedProps[name] = objectType.name;
};
}
export default class Serializable {
public static __serializableObjects: any = {};
private constructor() {
// don't inherit from me!
}
static serializeObject(typedObject: object) {
return JSON.stringify(typedObject, (key, value) => {
if (value) {
const proto = Object.getPrototypeOf(value);
if (proto?.__typeName)
value.__typeName = proto.__typeName;
}
return value;
}
);
}
static deserializeObject(typedObject: object, jsonObject: object): object {
const typeName = typedObject.__typeName;
return Object.assign(typedObject, this.assignTypeRecursion(typeName, jsonObject));
}
private static assignTypeRecursion(typeName, object): object {
const theObject = new Serializable.__serializableObjects[typeName]();
Object.assign(theObject, object);
const props = Object.getPrototypeOf(theObject).__serializedProps;
for (const property in props) {
const type = props[property];
try {
if (type == Array.name) {
const obj = object[property];
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; ++i) {
const arrItem = obj[i];
obj[i] = Serializable.assignTypeRecursion(arrItem.__typeName, arrItem);
}
} else
object[property] = [];
} else
object[property] = Serializable.assignTypeRecursion(type, object[property]);
} catch (e) {
console.error(`${e.message}: ${type}`);
}
}
return theObject;
}
}
Comments
Since I am a total js/ts newby (< 10 days), I am more than happy to receive any input/comments/suggestions. Here are some of my thoughts so far:
It could be cleaner: Unfortunately I did not find a way to get rid of the redundant parameter of #SerializableProp.
It could be more memory friendly: After you call serializeObject() every object stores __typeName which could massively blow up memory footprint. Fortunately __serializedProps is only stored once per class.
It could be more CPU friendly: It's the most inefficient code I've ever written. But well, it's just for web apps, so who cares ;-) Maybe one should at least get rid of the recursion.
Almost no error handling: well that's a task for another day
class A {
constructor (a) {
this.a = a
}
method1 () {
console.log('hi')
}
}
var b = new A(1)
b.method1() // hi
var c = JSON.stringify(b)
var d = JSON.parse(c)
console.log(d.a) // 1
try {
d.method1() // not a function
} catch {
console.log('not a function')
}
var e = Object.setPrototypeOf(d, A.prototype)
e.method1() // hi
Olivers answers is very clear, but if you are looking for a solution in angular js, I have written a nice module called Angular-jsClass which does this ease, having objects defined in litaral notation is always bad when you are aiming to a big project but saying that developers face problem which exactly BMiner said, how to serialize a json to prototype or constructor notation objects
var jone = new Student();
jone.populate(jsonString); // populate Student class with Json string
console.log(jone.getName()); // Student Object is ready to use
https://github.com/imalhasaranga/Angular-JSClass

How to get an object's methods?

Is there a method or propertie to get all methods from an object? For example:
function foo() {}
foo.prototype.a = function() {}
foo.prototype.b = function() {}
foo.get_methods(); // returns ['a', 'b'];
UPDATE: Are there any method like that in Jquery?
Thank you.
function getMethods(obj)
{
var res = [];
for(var m in obj) {
if(typeof obj[m] == "function") {
res.push(m)
}
}
return res;
}
Remember that technically javascript objects don't have methods. They have properties, some of which may be function objects. That means that you can enumerate the methods in an object just like you can enumerate the properties. This (or something close to this) should work:
var bar
for (bar in foo)
{
console.log("Foo has property " + bar);
}
There are complications to this because some properties of objects aren't enumerable so you won't be able to find every function on the object.
You can use console.dir(object) to write that objects properties to the console.
In modern browsers you can use Object.getOwnPropertyNames to get all properties (both enumerable and non-enumerable) on an object. For instance:
function Person ( age, name ) {
this.age = age;
this.name = name;
}
Person.prototype.greet = function () {
return "My name is " + this.name;
};
Person.prototype.age = function () {
this.age = this.age + 1;
};
// ["constructor", "greet", "age"]
Object.getOwnPropertyNames( Person.prototype );
Note that this only retrieves own-properties, so it will not return properties found elsewhere on the prototype chain. That, however, doesn't appear to be your request so I will assume this approach is sufficient.
If you would only like to see enumerable properties, you can instead use Object.keys. This would return the same collection, minus the non-enumerable constructor property.
for me, the only reliable way to get the methods of the final extending class, was to do like this:
function getMethodsOf(obj){
const methods = {}
Object.getOwnPropertyNames( Object.getPrototypeOf(obj) ).forEach(methodName => {
methods[methodName] = obj[methodName]
})
return methods
}
The methods can be inspected in the prototype chain of the object using the browser's developer tools (F12):
console.log(yourJSObject);
or more directly
console.dir(yourJSObject.__proto__);
In ES6:
let myObj = {myFn : function() {}, tamato: true};
let allKeys = Object.keys(myObj);
let fnKeys = allKeys.filter(key => typeof myObj[key] == 'function');
console.log(fnKeys);
// output: ["myFn"]
var funcs = []
for(var name in myObject) {
if(typeof myObject[name] === 'function') {
funcs.push(name)
}
}
I'm on a phone with no semi colons :) but that is the general idea.
var methods = [];
for (var key in foo.prototype) {
if (typeof foo.prototype[key] === "function") {
methods.push(key);
}
}
You can simply loop over the prototype of a constructor and extract all methods.
the best way is:
let methods = Object.getOwnPropertyNames(yourobject);
console.log(methods)
use 'let' only in es6, use 'var' instead
In Chrome is keys(foo.prototype). Returns ["a", "b"].
See: https://developer.chrome.com/devtools/docs/commandline-api#keysobject
Later edit: If you need to copy it quick (for bigger objects), do copy(keys(foo.prototype)) and you will have it in the clipboard.
Get the Method Names:
var getMethodNames = function (obj) {
return (Object.getOwnPropertyNames(obj).filter(function (key) {
return obj[key] && (typeof obj[key] === "function");
}));
};
Or, Get the Methods:
var getMethods = function (obj) {
return (Object.getOwnPropertyNames(obj).filter(function (key) {
return obj[key] && (typeof obj[key] === "function");
})).map(function (key) {
return obj[key];
});
};

Categories

Resources