JavaScript || Convert & Deserialize JSON & Simple Object To A Class Type Using Vanilla JavaScript
The following is a module which demonstrates how to convert a simple object to a class type, as well as parsing a JSON serialized object back to its class type.
1. Convert Simple Object To Typed Class
The example below demonstrates how to convert a simple object to its class type. Converting the simple object to its class type allows us to use its class functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// Convert Simple Object To Class <script> class Person { constructor(firstName, lastName, address) { this.firstName = firstName; this.lastName = lastName; this.address = address } getFullName() { return `${this.firstName} ${this.lastName}`; } } (() => { // Convert simple object to class object let personObj = { firstName: 'Kenneth', lastName: 'P' }; // Convert to the class type 'Person' allowing // us to use the functions from that class let personType = Utils.ctype(personObj, Person); console.log(personType.getFullName()); })(); </script> // expected output: /* Kenneth P */ |
The ‘Utils.ctype‘ function converts the object to a class, which allows to use the functions from that class.
2. Deserialize Simple JSON Object To Typed Class
Parsing a simple JSON string and converting it to a typed class can be achieved in a similar way. The example below demonstrates this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Parse Simple JSON Object To Class <script> class Person { constructor(firstName, lastName, address) { this.firstName = firstName; this.lastName = lastName; this.address = address } getFullName() { return `${this.firstName} ${this.lastName}`; } } (() => { // Convert Json to class object let personJson = ` { "firstName": "Kenneth", "lastName": "P" } `; // Convert to the class type 'Person' allowing // us to use the functions from that class let personType = Utils.ctype(JSON.parse(personJson), Person); console.log(personType.getFullName()); })(); </script> // expected output: /* Kenneth P */ |
3. Deserialize Simple Nested JSON Object To Typed Class
Parsing a multi-leveled, nested JSON string and converting it to a typed class is done a little differently. We have to map the JSON structure with the values we want to convert. This is done using ‘Utils.ctypeMap‘.
The example below demonstrates this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// Parse Nested JSON Object To Class <script> class Person { constructor(firstName, lastName, address) { this.firstName = firstName; this.lastName = lastName; this.address = address } getFullName() { return `${this.firstName} ${this.lastName}`; } } class Address { constructor(street, city, zip) { this.street = street; this.city = city; this.zip = zip; } getFullAddress() { return `${this.street} - ${this.city}, ${this.zip}`; } } (() => { // Convert Json to class object let personJson = ` { "firstName": "Kenneth", "lastName": "P", "address": { "street": "Valley Hwy", "city": "Valley Of Fire", "zip": "89040" } } `; // Create a conversion type map let conversionTypeMap = { '': Person, // Convert the entire object to type 'Person' 'address': Address, // Convert the address property to type 'Address' }; // Deserialize the json string and convert the types let personType = Utils.ctypeMap(JSON.parse(personJson), conversionTypeMap); console.log(personType.getFullName()); console.log(personType.address.getFullAddress()); })(); </script> // expected output: /* Kenneth P Valley Hwy - Valley Of Fire, 89040 */ |
4. Deserialize Complex Nested JSON Object To Typed Class
Parsing a complex JSON string and converting it to a typed class can be achieved in a similar way. The example below demonstrates this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
// Parse Nested JSON Object To Class <script> class Address { constructor(street, city, zip) { this.street = street; this.city = city; this.zip = zip; } getFullAddress() { return `${this.street} - ${this.city}, ${this.zip}`; } } class Person { constructor(firstName, lastName, address) { this.firstName = firstName; this.lastName = lastName; this.address = address } getFullName() { return `${this.firstName} ${this.lastName}`; } } class Album { constructor(artist, title, released) { this.artist = artist; this.title = title; this.released = released } getDescription() { return `${this.artist}: ${this.title} - ${this.released}`; } } (() => { // Convert Json object array to class object let personJson = `{ "people": [ { "firstName": "Kenneth", "lastName": "P", "address": { "street": "Valley Hwy", "city": "Valley Of Fire", "zip": "89040" }, "albums": [ { "artist": "System of a Down", "title": "Toxicity", "released": 2001 }, { "artist": "Kanye West", "title": "The College Dropout", "released": 2004 } ] }, { "firstName": "Jennifer", "lastName": "N", "address": { "street": "Boulder Ln", "city": "Yosemite Valley", "zip": "95389" }, "albums": [ { "artist": "Coldplay", "title": "Parachutes", "released": 2000 }, { "artist": "Alicia Keys", "title": "Songs in A Minor", "released": 2001 } ] } ], "nested": { "albums": [ { "artist": "Coldplay", "title": "Parachutes", "released": 2000 }, { "artist": "Alicia Keys", "title": "Songs in A Minor", "released": 2001 }, { "artist": "System of a Down", "title": "Toxicity", "released": 2001 }, { "artist": "Kanye West", "title": "The College Dropout", "released": 2004 } ] }, "nested2": { "album": { "artist": "Coldplay", "title": "Parachutes", "released": 2000 } } }`; // Create a conversion type map let conversionTypeMap = { 'people.albums': Album, // Convert all the albums in the person class array to type 'Album' 'people[0].address': Address, // Convert only the first address object of the people array to type 'Address' 'people.address': Address, // Convert all address objects of the people array to type 'Address' 'people': Person, // Convert all the objects in the people array to type 'Person' 'nested.albums': Album, // Convert all the albums objects in the nested object to type 'Album' 'nested2.album': Album, // Convert all the albums in the nested2 object to type 'Album' }; // Deserialize the json string and convert the types let deserializedData = Utils.ctypeMap(JSON.parse(personJson), conversionTypeMap); // Print the converted addresses using the class function console.log('\nPeoples Albums:'); deserializedData.people.forEach((person, index) => { console.log((index + 1) + '. ' + person.getFullName() + '\'s Albums:'); person.albums.forEach((album, index) => { console.log(`\t${index + 1}. ${album.getDescription()}`); }); }); // Print the first address object console.log('\nFirst address object'); console.log(deserializedData.people[0].address.getFullAddress()); // Print the converted people using the class function console.log('\nPeople:'); deserializedData.people.forEach((person, index) => { console.log(`${index + 1}. ${person.getFullName()}`); }); // Print the converted albums using the class function console.log('\nNested Albums:'); deserializedData.nested.albums.forEach((album, index) => { console.log(`${index + 1}. ${album.getDescription()}`); }); // Print the converted addresses using the class function console.log('\nPeoples Address:'); deserializedData.people.forEach((person, index) => { console.log(`${index + 1}. ${person.address.getFullAddress()}`); }); console.log('\nNested 2'); console.log(deserializedData.nested2.album.getDescription()); })(); </script> // expected output: /* Peoples Albums: 1. Kenneth P's Albums: 1. System of a Down: Toxicity - 2001 2. Kanye West: The College Dropout - 2004 2. Jennifer N's Albums: 1. Coldplay: Parachutes - 2000 2. Alicia Keys: Songs in A Minor - 2001 First address object Valley Hwy - Valley Of Fire, 89040 People: 1. Kenneth P 2. Jennifer N Nested Albums: 1. Coldplay: Parachutes - 2000 2. Alicia Keys: Songs in A Minor - 2001 3. System of a Down: Toxicity - 2001 4. Kanye West: The College Dropout - 2004 Peoples Address: 1. Valley Hwy - Valley Of Fire, 89040 2. Boulder Ln - Yosemite Valley, 95389 Nested 2 Coldplay: Parachutes - 2000 */ |
5. Utils.ctype/ctypeMap Namespace
The following is the Utils.js Namespace. Include this in your project to start using!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
// ============================================================================ // Author: Kenneth Perkins // Date: Jul 14, 2020 // Taken From: http://programmingnotes.org/ // File: Utils.js // Description: Javascript that handles general utility functions // ============================================================================ /** * NAMESPACE: Utils * USE: Handles general utility functions. */ var Utils = Utils || {}; (function(namespace) { 'use strict'; // -- Public data -- // Property to hold public variables and functions let exposed = namespace; /** * FUNCTION: ctype * USE: Returns the result of explicitly converting an expression * to a specified data type or class * @param expression: The object to be converted. * @param typename: The data type or class to be converted to. * @return: The converted value. */ exposed.ctype = (expression, typename) => { let value = expression; if (!exposed.isType(expression, typename)) { value = Object.create(typename.prototype, Object.getOwnPropertyDescriptors(expression)); } return value; } /** * FUNCTION: ctypeMap * USE: Returns the result of explicitly converting an expression * to a specified data type or class * @param expression: The object to be converted. * @param conversionTypeMap: An object that specifies the data type * or class to be converted to. * @return: The converted value. */ exposed.ctypeMap = (expression, conversionTypeMap) => { // Go through each property and convert // objects to the class specific types for (const prop in conversionTypeMap) { let type = conversionTypeMap[prop]; if (prop.trim().length < 1) { expression = exposed.ctype(expression, type); continue; } let items = getSetDescendantProp(expression, prop); if (!items) { continue; } else if (!Array.isArray(items)) { items = [items]; } items.forEach((obj, index) => { if (exposed.isType(obj, type)) { return; } getSetDescendantProp(expression, prop, obj, exposed.ctype(obj, type)); }); } return expression; } exposed.isType = (expression, typename) => { return (expression instanceof typename); } // -- Private data -- let getSetDescendantProp = (obj, desc, prevValue, newValue) => { let arr = desc ? desc.split('.') : []; // Go through the item properties and try to // find the item that matches 'desc' while (arr.length && obj) { let comp = arr.shift(); // Handle full arrays let target = obj[comp]; if (target && ((target.length && target.forEach) || Array.isArray(target))) { let remainder = arr.join('.'); let results = []; for (let index = 0; index < target.length; ++index){ let x = getSetDescendantProp(target[index], remainder, prevValue, newValue); if (x) { results = results.concat(x); } if (remainder.length < 1 && typeof newValue !== 'undefined') { if (prevValue === target[index]) { target[index] = newValue; } } } return results; } else { // Handle indexed arrays let match = new RegExp('(.+)\\[([0-9]*)\\]').exec(comp); if ((match !== null) && (match.length == 3)) { let arrayData = { arrName: match[1], arrIndex: match[2] }; if (obj[arrayData.arrName] !== undefined) { if (typeof newValue !== 'undefined' && arr.length === 0 && prevValue === obj[arrayData.arrName][arrayData.arrIndex]) { obj[arrayData.arrName][arrayData.arrIndex] = newValue; } // Move to the next item obj = obj[arrayData.arrName][arrayData.arrIndex]; } else { obj = undefined; } } else { // Handle regular items if (typeof newValue !== 'undefined') { if (obj[comp] === undefined) { obj[comp] = {}; } if (arr.length === 0 && prevValue === obj[comp]) { obj[comp] = newValue; } } // Move to the next item obj = obj[comp]; } } } return obj; } (function (factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } }(function() { return namespace; })); }(Utils)); // http://programmingnotes.org/ |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
Leave a Reply