Parse JSON String into a Particular Object Prototype in JavaScript
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)
Safely turning a JSON string into an object
JSON.parse(jsonString)
is a pure JavaScript approach so long as you can guarantee a reasonably modern browser.
JSON.parse(str) somehow retrieved very specific information of __proto__ of original object, which is not in the JSON string(=str). How?
So it seems that I misused prototype.
Correct way to delete age : 30 would be
Either
delete user1.__proto__.age;
Or
delete Object.prototype.age;
And here user1=null
is irrelevant.
For some reason user1.__proto__ = null
achieves nothing and it would be great if someone hints me on why
Edit: user1.__proto__ = null
does remove proto of user1 but it doesn't affect Object.prototype. I think it only detaches user1 from prototype chain.
How to parse JSON string in Typescript
Typescript is (a superset of) javascript, so you just use JSON.parse
as you would in javascript:
let obj = JSON.parse(jsonString);
Only that in typescript you can have a type to the resulting object:
interface MyObj {
myString: string;
myNumber: number;
}
let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }');
console.log(obj.myString);
console.log(obj.myNumber);
(code in playground)
How to make a JSON parse object prototype less in Javascript?
JSON.parse() method accepts a callback that could be used to transform object into prototypeless object.
JSON.parse(string, function(k, v) {
if (v && typeof v === 'object' && !Array.isArray(v)) {
return Object.assign(Object.create(null), v);
}
return v;
});
https://esdiscuss.org/topic/proposal-add-an-option-to-omit-prototype-of-objects-created-by-json-parse
Turning JSON strings into objects with methods
To do this, you'll want to use a "reviver" function when parsing the JSON string (and a "replacer" function or a toJSON
function on your constructor's prototype when creating it). See Section 15.12.2 and 15.12.3 of the specification. If your environment doesn't yet support native JSON parsing, you can use one of Crockford's parsers (Crockford being the inventor of JSON), which also support "reviver" functions.
Here's a simple bespoke example that works with ES5-compliant browsers (or libraries that emulate ES5 behavior) (live copy, run in Chrome or Firefox or similar), but look after the example for a more generalized solution.
// Our constructor function
function Foo(val) {
this.value = val;
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.toJSON = function() {
return "/Foo(" + this.value + ")/";
};
// An object with a property, `foo`, referencing an instance
// created by that constructor function, and another `bar`
// which is just a string
var obj = {
foo: new Foo(42),
bar: "I'm bar"
};
// Use it
display("obj.foo.value = " + obj.foo.value);
display("obj.foo.nifty = " + obj.foo.nifty);
display("obj.bar = " + obj.bar);
// Stringify it with a replacer:
var str = JSON.stringify(obj);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var obj2 = JSON.parse(str, function(key, value) {
if (typeof value === "string" &&
value.substring(0, 5) === "/Foo(" &&
value.substr(-2) == ")/"
) {
return new Foo(value.substring(5, value.length - 2));
}
return value;
});
// Use the result
display("obj2.foo.value = " + obj2.foo.value);
display("obj2.foo.nifty = " + obj2.foo.nifty);
display("obj2.bar = " + obj2.bar);
Note the toJSON
on Foo.prototype
, and the function we pass into JSON.parse
.
The problem there, though, is that the reviver is tightly coupled to the Foo
constructor. Instead, you can adopt a generic framework in your code, where any constructor function can support a fromJSON
(or similar) function, and you can use just one generalized reviver.
Here's an example of a generalized reviver that looks for a ctor
property and a data
property, and calls ctor.fromJSON
if found, passing in the full value it received (live example):
// A generic "smart reviver" function.
// Looks for object values with a `ctor` property and
// a `data` property. If it finds them, and finds a matching
// constructor that has a `fromJSON` property on it, it hands
// off to that `fromJSON` fuunction, passing in the value.
function Reviver(key, value) {
var ctor;
if (typeof value === "object" &&
typeof value.ctor === "string" &&
typeof value.data !== "undefined") {
ctor = Reviver.constructors[value.ctor] || window[value.ctor];
if (typeof ctor === "function" &&
typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value);
}
}
return value;
}
Reviver.constructors = {}; // A list of constructors the smart reviver should know about
To avoid having to repeat common logic in toJSON
and fromJSON
functions, you could have generic versions:
// A generic "toJSON" function that creates the data expected
// by Reviver.
// `ctorName` The name of the constructor to use to revive it
// `obj` The object being serialized
// `keys` (Optional) Array of the properties to serialize,
// if not given then all of the objects "own" properties
// that don't have function values will be serialized.
// (Note: If you list a property in `keys`, it will be serialized
// regardless of whether it's an "own" property.)
// Returns: The structure (which will then be turned into a string
// as part of the JSON.stringify algorithm)
function Generic_toJSON(ctorName, obj, keys) {
var data, index, key;
if (!keys) {
keys = Object.keys(obj); // Only "own" properties are included
}
data = {};
for (index = 0; index < keys.length; ++index) {
key = keys[index];
data[key] = obj[key];
}
return {ctor: ctorName, data: data};
}
// A generic "fromJSON" function for use with Reviver: Just calls the
// constructor function with no arguments, then applies all of the
// key/value pairs from the raw data to the instance. Only useful for
// constructors that can be reasonably called without arguments!
// `ctor` The constructor to call
// `data` The data to apply
// Returns: The object
function Generic_fromJSON(ctor, data) {
var obj, name;
obj = new ctor();
for (name in data) {
obj[name] = data[name];
}
return obj;
}
The advantage here being that you defer to the implementation of a specific "type" (for lack of a better term) for how it serializes and deserializes. So you might have a "type" that just uses the generics:
// `Foo` is a constructor function that integrates with Reviver
// but doesn't need anything but the generic handling.
function Foo() {
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.spiffy = "I'm the spiffy inherited property.";
Foo.prototype.toJSON = function() {
return Generic_toJSON("Foo", this);
};
Foo.fromJSON = function(value) {
return Generic_fromJSON(Foo, value.data);
};
Reviver.constructors.Foo = Foo;
...or one that, for whatever reason, has to do something more custom:
// `Bar` is a constructor function that integrates with Reviver
// but has its own custom JSON handling for whatever reason.
function Bar(value, count) {
this.value = value;
this.count = count;
}
Bar.prototype.nifty = "I'm the nifty inherited property.";
Bar.prototype.spiffy = "I'm the spiffy inherited property.";
Bar.prototype.toJSON = function() {
// Bar's custom handling *only* serializes the `value` property
// and the `spiffy` or `nifty` props if necessary.
var rv = {
ctor: "Bar",
data: {
value: this.value,
count: this.count
}
};
if (this.hasOwnProperty("nifty")) {
rv.data.nifty = this.nifty;
}
if (this.hasOwnProperty("spiffy")) {
rv.data.spiffy = this.spiffy;
}
return rv;
};
Bar.fromJSON = function(value) {
// Again custom handling, for whatever reason Bar doesn't
// want to serialize/deserialize properties it doesn't know
// about.
var d = value.data;
b = new Bar(d.value, d.count);
if (d.spiffy) {
b.spiffy = d.spiffy;
}
if (d.nifty) {
b.nifty = d.nifty;
}
return b;
};
Reviver.constructors.Bar = Bar;
And here's how we might test that Foo
and Bar
work as expected (live copy):
// An object with `foo` and `bar` properties:
var before = {
foo: new Foo(),
bar: new Bar("testing", 42)
};
before.foo.custom = "I'm a custom property";
before.foo.nifty = "Updated nifty";
before.bar.custom = "I'm a custom property"; // Won't get serialized!
before.bar.spiffy = "Updated spiffy";
// Use it
display("before.foo.nifty = " + before.foo.nifty);
display("before.foo.spiffy = " + before.foo.spiffy);
display("before.foo.custom = " + before.foo.custom + " (" + typeof before.foo.custom + ")");
display("before.bar.value = " + before.bar.value + " (" + typeof before.bar.value + ")");
display("before.bar.count = " + before.bar.count + " (" + typeof before.bar.count + ")");
display("before.bar.nifty = " + before.bar.nifty);
display("before.bar.spiffy = " + before.bar.spiffy);
display("before.bar.custom = " + before.bar.custom + " (" + typeof before.bar.custom + ")");
// Stringify it with a replacer:
var str = JSON.stringify(before);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var after = JSON.parse(str, Reviver);
// Use the result
display("after.foo.nifty = " + after.foo.nifty);
display("after.foo.spiffy = " + after.foo.spiffy);
display("after.foo.custom = " + after.foo.custom + " (" + typeof after.foo.custom + ")");
display("after.bar.value = " + after.bar.value + " (" + typeof after.bar.value + ")");
display("after.bar.count = " + after.bar.count + " (" + typeof after.bar.count + ")");
display("after.bar.nifty = " + after.bar.nifty);
display("after.bar.spiffy = " + after.bar.spiffy);
display("after.bar.custom = " + after.bar.custom + " (" + typeof after.bar.custom + ")");
display("(Note that after.bar.custom is undefined because <code>Bar</code> specifically leaves it out.)");
How do I cast a JSON Object to a TypeScript class?
You can't simple cast a plain-old-JavaScript result from an Ajax request into a prototypical JavaScript/TypeScript class instance. There are a number of techniques for doing it, and generally involve copying data. Unless you create an instance of the class, it won't have any methods or properties. It will remain a simple JavaScript object.
While if you only were dealing with data, you could just do a cast to an interface (as it's purely a compile time structure), this would require that you use a TypeScript class which uses the data instance and performs operations with that data.
Some examples of copying the data:
- Copying AJAX JSON object into existing Object
- Parse JSON String into a Particular Object Prototype in JavaScript
In essence, you'd just :
var d = new MyRichObject();
d.copyInto(jsonResult);
What are prototypes and why are they added to my json object
Don't worry, you can ignore it.
JavaScript uses prototypical inheritance: An object inherits features from another object, called its prototype (which may, in turn, have its own prototype, and so on). It's possible to create an object that doesn't have a prototype, but by default, all objects do.
Plain objects in JavaScript inherit from the basic object prototype (which you can access via the Object.prototype
property): When your JSON is parsed and the plain objects for its contents are created, their prototype is that object, which provides the toString
, valueOf
, and hasOwnProperty
, and other methods and (on browsers) the __proto__
property (though you should pretend that last one isn't there):
const json = "{}";const obj = JSON.parse(json);console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
Related Topics
Remove All Special Characters with Regexp
How to Load Local Script Files as Fallback in Cases Where Cdn Are Blocked/Unavailable
Safari 3Rd Party Cookie Iframe Trick No Longer Working
JavaScript - Get Portion of Url Path
Where to Write to Localstorage in a Redux App
JavaScript Math, Round to Two Decimal Places
Angularjs - Any Way for $Http.Post to Send Request Parameters Instead of JSON
Communication Between Sibling Components in Vue.Js 2.0
Get the Value of Checked Checkbox
JavaScript - Get the First Day of the Week from Current Date
Can Es6 Template Literals Be Substituted at Runtime (Or Reused)
How to Convert Date to Timestamp
Onchange Event Using React Js for Drop Down
Nodejs Callbacks Simple Example
Check If Event Exists on Element
How to Use Revealing Module Pattern in JavaScript
Check If an Element Is Present in an Array
How to Import Es6 Modules in Content Script for Chrome Extension