Custom object to JSON then back to a custom object? - javascript

I've seen very similar questions to this, but I can't quite decide if they was answered clearly - maybe I'm being a bit dense, sorry.
I want to have the convenience (and clarity) of my own object, call it a CardboardBox(). It won't contain code, just data. I want to write this to a database and read it back later, but obviously, it is a type Object() when it's read back. All I can think of to find out what it used to be is:
Have a member variable type that I set to CARDBOARD_BOX
Instantiate a new CarbardBox() and use a function (in the box) to copy the properties of Object() to the new CardboardBox() object
Is there a better way of doing this? I'm pretty sure I can change the actual type.
function CardboardBox() {
this.type = "CARDBOARD_BOX"
this.name = "No set";
this.populate = new function(obj) {
// populate this object with obj properties
}
var box = new CarboardBox(); // CarboardBox
box.name = "My Box";
send = JSON.stringyfy(box);
.
.
.
obj = JSON.parse(send); // Object
if (obj.type == "CARDBOARD_BOX") {
savedBox = new CardboardBox();
savedBox.populate(obj);
}
Thanks in advance...
Steve
[edit] My test code.
function CardboardBox(n) {
this.name = n;
}
var box = new CardboardBox("My Box");
send = JSON.stringify(box); // JSON CarboardBox()
obj = JSON.parse(send, function fn(obj) { // Object() returned
log("OB: "+obj.type);
return obj.type === 'CardboardBox' ? new CardboardBox(obj) : CardboardBox;
});
console.log(obj);
Output is:
OB: undefined utils.js:40
OB: undefined utils.js:40
function CardboardBox(n) {
this.name = n;
}

One possible solution is the following:
function CardboardBox(n) {
if(typeof(n) == 'string') {
//build from name string
this.name = n;
} else {
//build from object
this.name = n.name;
}
//add in this object's "type" in a place
//that is unlikely to exist in other JSON strings
this.__type = 'CardboardBox';
}
var box = new CardboardBox("My Box");
send = JSON.stringify(box), // JSON CarboardBox()
obj = JSON.parse(send, function(key, val) {
//if this is an object, and is CardboardBox
if(typeof(val) === 'object' && val.__type === 'CardboardBox')
return new CardboardBox(val);
return val;
//or if your object is in a context (like window), and there are many of
//them that could be in there, you can do:
//
//if(typeof(val) === 'object' && context[val.__type])
// return new context[val.__type](val);
});
console.log(obj);
Basically store the object type in a place you know to look for later on when parsing the json. if you have multiple objects you can instantiate in a single scope the second parse method may be more appropriate. This also will account for objects in the JSON that are not CarboardBoxs.
Edit Here is a jsFiddle of this method in action.

Overall, you're correct: Javascript doesn't have any built-in way to serialize anything beyond plain objects, so going to and from JSON will not produce a particular class when you deserialize it. So you need to either work out serialization/deserialization yourself, or use a library that provides some support.
I personally like Backbone.js for this problem, as it handles serializing and deserializing quite well. You define a model class, which include a method to save its data to a server in a serialized form, and a method to deserialize it back to the model. The key design issue here is that deserializing is performed knowing the model you're deserializing to:
you either call myModel.fetch() to get data from the server based on the model id, or
you pass a bunch of new data to the model constructor: new Model(serializedData), or
you pass an array of data for multiple models to a collection that knows the model type: new ModelCollection(arrayOfSerializedData).
What Backbone doesn't do is deal with type-casting data of an unknown type. When I've dealt with this, I've usually done something similar to #Chad's response, but using an intermediary; you could see this as a proxy model, or as a factory:
var classes = {
CardboardBox: ...,
AluminumBox: ...
}
function Deserializer(json) {
// parse if you're actually dealing with a string
var data = JSON.parse(json),
// now look for some custom type flag - you'll need to set this yourself
type = data.type,
// class lookup, perhaps with a default
Cls = classes[type] || DefaultType;
return new Cls(data);
}
var obj = new Deserializer(send);
obj instanceof CardboardBox; // should work
This still relies on a custom flag to switch types, though - I'm not sure there's any way around this.

Related

Create a json object with properties values different from previous for sending as a message

I am delivering data from PLC Siemens S7 through node-red installed on IoT edge device to the cloud storage.
In the node-red I receive data as a JSON object with several properties every second from PLC node. The JSON object (or data) has the same properties every second.
Some properties change every second, some change in five minute, some remain constant for several hours.
To handle data to the cloud the JSON object is handled to the IoT hub node in the node-red.
I would like to process this JSON object before handling to the IoT hub node in a way that the JSON object has only the properties which changed from previous state (a second ago). It means that I want to compare an input object at the moment t with the object at t-1 and generate a new object with properties of the object at t which differ from those of the object at t-1.
The new object will be handled further. The object processing is expected to be done in the function node in the node-red. From PLC node the JSON object is received as the msg.payload.
Could you please help me to script in (javascript) the process I have described?
msg.payload to the function node is json object:
{"Zone0": 200,
"Wire_tension": 2.5}
UPD: Since it was not clear what I have tried, here is the code. Sorry for not providing it, I thought it was clear.
var state = {}; // this state will be messaged further
var prev_state = {
"Zone0": 0,
"Wire_tension": 0
}; // initially zeros are assigned
var current_state = msg.payload; // has these two properties and updated every second
// comparison of properties
if (current_state.Zone0 != prev_state.Zone0) {
state["Zone0"] = current_state.Zone0;
prev_state["Zone0"] = current_state.Zone0;
}
if (current_state.Wire_tension != prev_state.Wire_tension) {
state["Wire_tension"] = current_state.Wire_tension;
prev_state["Wire_tension"] = current_state.Wire_tension;
}
msg.payload = state;
return msg;
It looks like when the flow in the node-red starts the function node runs the code every time. Therefore prev_state is assigned every time. I understand that I need to somehow handle initial state which is updated further.
UPD2:
I also tried using the following code (also with context feature). The example is with my real data:
var state = {}; // this state will be messaged further
flow.set(["Zone0", "Wire_tension"],[0,0]); // initially zeros are assigned
var current_state = msg.payload; // has these two properties and updated every second
// comparison of properties
if (current_state.Zone0 != flow.get("Zone0")) {
state["Zone0"] = current_state.Zone0;
flow.set("Zone0", current_state.Zone0);
}
if (current_state.Wire_tension != flow.get("Wire_tension")) {
state["Wire_tension"] = current_state.Wire_tension;
flow.set("Wire_tension", current_state.Wire_tension);
}
msg.payload = state;
return msg;
best option is use node-red global context variable
var object_to_build = {}
var key_1_val = global.get("key_1");
if (msg.payload.key_1 != key_1_val) {
object_tto_build['key_1'] = msg.payload.key_1
global.set("key_1", msg.payload.key_1);
}
// do whatever you like to do with - object_to_build. I have not validated the code, just the typo
In the "On Start" tab in properties of function node:
flow.set(["Zone0", "Wire_tension"], [0, 0]);
In the "On Message" tab in the properties of function node:
var state = {}; // this state will be messaged further
var current_state = msg.payload; // has these two properties and updated every second
// comparison of properties
if (current_state.Zone0 != context.get("Zone0")) {
state["Zone0"] = current_state.Zone0;
context.set("Zone0", current_state.Zone0);
}
if (current_state.Wire_tension != context.get("Wire_tension")) {
state["Wire_tension"] = current_state.Wire_tension;
context.set("Wire_tension", current_state.Wire_tension);
}
msg.payload = state;
return msg;

How do make a copy of a javascript object which only retains data, but not functions?

I am preparing big objects and so I have also added some functions to maintain them. I need to send this object across to serve incoming queries. I don't see any benefit in sending the functions too. I just want to send the data only. How do I strip an object of its functions, to make it leaner while sending.
EDIT : confirming using JSON.stringify and JSON.parse works
l={}
Object {}
l.zz=19
19
l.f = function(){ console.log(this.zz) }
function (){ console.log(this.zz) }
l
Object {zz: 19, f: function}
JSON.stringify(l)
"{"zz":19}"
JSON.parse(JSON.stringify(l))
Object {zz: 19}
Just take JSON.stringify() to make a string and parse it.
JSON.parse(JSON.stringify(obj))
Basically you use JSON.stringify to make the object into a JSON string. It will put all public fields(assigned by this) that are not functions but values into the JSON string.
By then taking the output of JSON.stringify and putting it into JSON.parse you get a clean object without functions but purely values.
Basically what you only need to do is:
var clean = JSON.parse(JSON.stringify(objectvariable));
Here is some example code splitting everything out step by step :-)
Hit F12 to see the output in console.
/** Sample object with functions and values **/
function ObjectHolder() {
this.x = "hello";
this.y = "world";
this.z = "universe";
this.which = false;
}
/** Make a string defined by boolean **/
ObjectHolder.prototype.toString = function() {
return this.x + " " +(this.which? this.y:this.z);
}
/** Sample object with functions and values **/
ObjectHolder.prototype.switchIt = function() {
this.which = !this.which;
}
/** instantiate object **/
var takeit = new ObjectHolder();
/** Show current output, to see if it works **/
console.log(takeit.toString());
/** Test inversion **/
takeit.switchIt();
/** output should be changed **/
console.log(takeit.toString());
/** Take the object and make it a string **/
var str = JSON.stringify(takeit);
/** Take the JSON string and turn it into an object **/
var obj = JSON.parse(str);
/** Show the differences between the values **/
console.log("ObjectHolder(var takeit) object with functions",takeit);
console.log("JSON string",str);
console.log("functionless object",obj);
Make a string and parse it:
JSON.parse(JSON.stringify(object))
This function would perform a deep copy of data properties letting functions out:
function deepCopyObject(src)
{
var tgt=[]
for (var x in src)
{
var p=src[x]
if (!(p instanceof Function))
{
if (p instanceof Object)
{
tgt[x]=copyObject(p)
}
else
{
tgt[x]=p
}
}
}
return tgt
}
To use it, just execute:
var myCopy=deepCopyObject(mySrc)

Deserializing JSON from local storage to Fabric JS object - JSON created from form data

I'm using Fabric js for a project I'm working on and encountering a few difficulties. My aim is to allow a user to enter details into a form. The form will then be turned into a JSON object and stored using local storage.
Upon pressing submit the user is taken to a new page where the JSON is retrieved and objects are created.
I'm having difficulty with the latter part. Getting the information from the form into JSON is working fine, various tests show that the output is as it should be.
When creating the object there are problems, the object is showing on the canvas as a simple border with corners, it can be moved and selected but all other attributes such as size and colour are lost. I think it's because of how the JSON is being deserilized. I've searched for a solution but yet to find one, any help is hugely appreciated.
Sample code:
This is my test instance that creates an object as expected:
var a = {
type: "rect",
left:200,
top:110,
fill:'red',
width:80,
height:50
};
The same as above but from the local storage:
[Log] From JSON: {"type":"rect","top":"110","left":"200","fill":"red","height":"50","width":"80"} (sim.html, line 135)
To get the data I'm using:
var test = localStorage.getItem("Number1");
console.log("From JSON: " +test);
var obj = JSON && JSON.parse(test) || $.parseJSON(test);
console.log(obj.type +"-"+ obj.left);
When a is added in the below method it works yet obj does not. The main difference I can see if the quotations but I'm not sure if that is the issue and if so how to remove it simply.
Function to iterate over and render objects:
fabric.util.enlivenObjects([circle1, obj], function(objects) {
var origRenderOnAddRemove = canvas.renderOnAddRemove;
canvas.renderOnAddRemove = false;
objects.forEach(function(o) {
canvas.add(o);
});
canvas.renderOnAddRemove = origRenderOnAddRemove;
canvas.renderAll();
});
Many thanks for any help.
I think you can modify your "serialize" routine so that it converts appropriate inputs to numbers:
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
var value = this.value || '';
if (/^\d+$/.test(value))
value = +value;
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(value);
} else {
o[this.name] = value;
}
}} );
return o;
};
That looks for input values that look like numbers and converts them. (It only looks for integers; if you wanted to also test for numbers with decimal fractions the regular expression could be modified.)
This isn't the most robust thing in the world, since it's making a fairly big assumption. To be more accurate you'd have to do something like tag your form inputs with a class to identify whether they're supposed to be treated as numeric, and then implement your own serializer to traverse all the inputs.

data cleansing javascript objects

I have a http server built in node.js and 'connect'. The web service currently parses JSON requests to an Object, does "stuff", and returns a synchronous response. The JSON is from an ecommerce cart and I end up with an Object that looks like (abridged version):
var myObj = {"request":{"industry":"social","transactionId":"gdfs23s","billing": {"addressLine1":"911 Fallen Street","addressLine2":"1 2"},"shipping":{"addressLine1":"1523 Willow Tree LAne","addressLine2":"unit 15"}}}
I want to clean up the data, performing tasks such as removing extra white space, normalizing postal abbreviation, ie street to st, etc.
I've written a series of regular expression that successfully achieve the cleansing/normalization.
However what I am unsure of is how to do this efficiently AND elegantly apply these cleanse processes to an Object in JS /Node.js. I will have scenarios where I want to cleanse request.billing.addressLine1, request.shipping.addressLine1 with the same regex pattern
I could of course do something like:
var addressCleaner= new RegExp("( str| street| strt)","g");
myObj.request.billing.addressLine1.replace(addressCleaner, "st");
myObj.request.shipping.addressLine1.replace(addressCleaner, "st");
But I dont feel this is very DRY and furthermore its not being done very "node"ishly.
Any suggestions or example approaches? I would like to avoid using a package like Mongoose etc. to do this as the type of normalizing i'm doing does not just consist of making sure a string is a string ,etc.
Thanks in advance.
So, I would suggest to have a hash with all normalizers, and seperately to have a list of properties,
which needs to be normalized. To have the idea here some code:
var Normalizers = {
// -> function
trim: function(str) { return str.trim(); },
// -> array [RegExp, StringToReplace]
street: [ /(str|street)/g, 'st']
//...
};
var properties = {
'request.billing.addressLine1': ['trim', 'street'],
// ..
};
obj_normalize(myObj, properties, Normalizers);
The code for obj_normalize/obj_getProprety/obj_setProperty I moved to the gist.
If your regex is applicable to every string found within the object you can simply recurse through the object and apply the regex to every string.
A general purpose object traversal function is very useful for something like this:
function object_traverse (name,obj,fn) {
obj = fn(name,obj);
if (obj instanceof Array) {
for (var n=0;n<obj.length;n++) {
obj[n] = object_traverse(n,obj[n],fn);
}
}
else if (typeof obj != "string" && typeof obj != "number") {
for (var n in obj) {
obj[n] = object_traverse(n,obj[n],fn);
}
}
return obj;
}
Now you can do this:
myObj = object_traverse('',myObj,function(name,obj){
if (typeof obj == "string" && name.match(/address/i)) {
obj = obj.replace(addressCleaner, "st");
}
return obj;
});
I'd have a model built from JSON files and serialize it as I see fit. This would avoid matching or searching for properties which couldn't possibly exist in the source. Some example:
function makeAddress(rawAddress) {
return { street: rawAddress["str"] ||
rawAddress["street"] ||
rawAddress["strt"],
. . . };
Being equipped with this function, say, then you have an array of "address" object, then converting them would be a matter of:
addresses.map(makeAddress);

Checking for types of objects in my script?

I'm writing a localStorage wrapper to make inserting and retrieving data easier when needing it to do more robust queries.
My script is here:
https://github.com/OscarGodson/storageLocker
A user of my script asked me how he could save a Date and i told him the proper procedure (save new Date().getTime() to the JSON and revive it with something like new Date(json.time) but he was trying to do hisStorage.save({'the_date':new Date()}), but to his surprise when he went to get the data it'd be malformed.
So, my question is, how can I catch inserts like this and other objects (maybe events?), convert them for the users to be stored in the JSON and also retrieved properly? I've been working on it all day and i can't figure out how to check for certain types of objects and convert accordingly via some switch case.
The save and retrieve part of my script looks like this:
Saving Data:
storageLocker.prototype.save = function(value){
var json = JSON.parse(localStorage.getItem(this.catalog));
if(json == null){json = {};}
for(var key in value){
if(value.hasOwnProperty(key)){
json[key] = value[key];
}
}
localStorage.setItem(this.catalog,JSON.stringify(json));
return this;
}
Getting Data:
storageLocker.prototype.get = function(value){
json = JSON.parse(localStorage.getItem(this.catalog));
if(json == null){json = {};}
if(value){
if(typeof json[value] !== 'undefined'){
return json[value];
}
else{
//Makes it so you can check with myStorage.get('thisWontExist').length
//and it will return 0 and typeof will return object.
return new Object('');
}
}
else{
return json;
}
};
Use the instanceof operator to check whether the property is an instance of Date:
if (value[key] instanceof Date) {
//serialize in some way
...
}else{
json[key] = value[key];
}
The problem is; in the getter, how are you going to know which values need to be revived again? You'll have to rely on some string format and accept that if the user saves a string in this format, then it will be revived as a date.

Categories