简体   繁体   中英

Mapping JSON Objects to Javascript Objects

When using AJAX, I tend to pass objects from my server to Javascript in the form of JSON objects (aka Javascript). Certain functions within my Javascript rely on the specific type of object I am using. For instance, lets use a phone number for example. I have a constructor:

function PhoneNumber(number, type, isPrimary, contactId, id, className) {
    this.number = number;
    this.type = type;
    this.isPrimary = isPrimary;
    this.contactId = contactId;
    this.id = id;
    this.className = className;
}

Which I use when creating a phone number object in my Javascript. In some situations I don't create the object in JS, I get the object from the server so it comes in the form of a generic object with the exact same fields. So when my code relies on the specific type by using something such as this:

var objType = objArray[i].constructor.name;
var mappedObj;

switch(objType) {
    case 'PhoneNumber':
        currentArray = currentArray.phone;
        //Convert response to javascript object.
        mappedObj = mapPhone(jsonResponse[i]);
        break;
    case 'Email':
        currentArray = currentArray.email;
        mappedObj = mapEmail(jsonResponse[i]);
        break;
    case 'Address':
        currentArray = currentArray.address;
        mappedObj = mapAddress(jsonResponse[i]);
        break;
    case 'Website':
        currentArray = currentArray.website;
        mappedObj = mapWebsite(jsonResponse[i]);
}

In this situation, I check the name of the objects constructor and set certain variables based on that name. If the object I check the name on is a JSON from the server, it simply gives me a generic "Object" response and thus the code does not work. I get around this by using a mapping function for each object such as:

function mapPhone(phoneObj) {
    var id = phoneObj.id;
    var contactId = phoneObj.contactId;
    var number = phoneObj.number;
    var type = phoneObj.type;
    var primary = phoneObj.isPrimary;
    var className = phoneObj.className;
    var phoneNumber = new PhoneNumber(number, type, primary, contactId, id, className);
    return phoneNumber;
}

This works just fine, but to me seems a little redundant. Is this the best way to solve the JSON Object problem, or is there a better solution? I understand this is more of a "Am I doing this the best way possible" type of question, but I repeat this type of logic CONSTANTLY in my Javascript code and I figure I might as well get another opinion or two on whether or not its the proper way to do this before I have to spend hour upon hour fixing it in the future.

EDIT: I ended up accepting a jQuery solution because I happen to use jQuery in my project. There are however multiple solutions that also worked for me before I found the jQuery option. They just weren't quite as clean and efficient.

The following requires you to have the same properties in your object and your JSON object.

var phoneNumber = $.extend(new PhoneNumber(), yourJSONObject);

This basically creates a new PhoneNumber object and then copies all properties from your JSON object onto it. The $.extend() method is from jQuery, but you could also use as similar method from eg Underscore.js or one of the other js libraries/frameworks.

This similar question has a lot of interesting answers:

Parse JSON String into a Particular Object Prototype in JavaScript

Based off the poster's own answer, I think this would be an effective solution for you:

function recastJSON(jsonObject) {
    // return generic object if objectType is not specified
    if (!jsonObject.objectType)
        return jsonObject;

    // otherwise create a new object of type specified
    var obj = eval('new '+jsonObject.objectType+'()');
    for(var i in jsonObject)
        obj[i] = jsonObject[i];
    return obj;
}

You will need to add objectType to the JSON objects you are receiving to define the javascript class you want to instantiate. Then when you call this function, it will cast the object to that type and copy the data over (including the variable 'objectType').

Using your phone number example, your code would look like this:

// generic object from JSON
var genericObject = {objectType:'PhoneNumber', number:'555-555-5555', type:'home', primary:true, contactId:123, id:1234, className:'stdPhone'};
var phoneObject = recastJSON(genericObject);

AFAIK, in everything that is not IE, you can do this:

// define a class
var Foo = function(name) {
  this.name = name;
}
// make a method
Foo.prototype.shout = function() {
  return "I am " + this.name;
}
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// force its class to be Foo
x.__proto__ = Foo.prototype;
// the method works
x.shout();

Unfortunately, IE does not support the __proto__ accessor, so what you would need to do is first create an empty instance of your object, then just copy everything over:

// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// make an empty Foo
var y = Object.create(Foo.prototype);
// copy stuff over
for (a in x) {
  y[a] = x[a];
}
y.shout();

Both of these approaches are quite a bit more generic than your mapWhatever functions, keeping it DRY.

If not supporting older browsers is ok, You can use Object.create to do the mapping for you . (dropping the shim—at least the shim at MDN—in will not fix older browsers, since that shim does not accept the second parameter.)

DEMO

function makeThisExtend(obj, CtorFunc) {
    for (var k in obj)
        if ({}.hasOwnProperty.call(obj, k))
            obj[k] = { value: obj[k] };

    return Object.create(CtorFunc.prototype, obj);
}

var objFromServer = { Number: "123", id: 5 };
objFromServer = makeThisExtend(objFromServer, PhoneNumber);


alert(objFromServer.Number + " " + objFromServer.id); //123 5
alert(objFromServer.constructor); //function PhoneNumber ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM