2010-05-14 2 views
17

Какой самый быстрый и простой способ конвертировать мой json, содержащий данные объектов, в реальные объекты с приложенными методами?Самый простой способ конвертировать данные json в объекты с помощью приложенных методов?

В качестве примера, я получаю данные для fruitbowl с массивом фруктовых объектов, которые в свою очередь содержат множество семян, таким образом:

{"fruitbowl": [{ 
    "name": "apple", 
    "color": "red", 
    "seeds": [] 
    },{ 
    "name": "orange", 
    "color": "orange", 
    "seeds": [ 
     {"size":"small","density":"hard"}, 
     {"size":"small","density":"soft"} 
    ]} 
} 

Это все красиво и хорошо, но вниз на клиенте мы делать вещи с этим фруктом, как съесть его и сажать дерева ...

var fruitbowl = [] 
function Fruit(name, color, seeds){ 
    this.name = name 
    this.color = color 
    this.seeds = seeds 
    this.eat = function(){ 
     // munch munch 
    } 
} 
function Seed(size, density){ 
    this.size = size 
    this.density = density 
    this.plant = function(){ 
      // grow grow 
    } 
} 

успеха рутины моего Аякса в настоящее время в настоящее время цикла над вещью и строительство каждого объекта в своей очереди, и он не обрабатывает семена еще, потому что перед тем, как я перейду к созданию семенного конструктора с Я думаю

Нет ли лучшего способа?

success: function(data){   
     fruitbowl.length = 0 
     $.each(data.fruitbowl, function(i, f){ 
      fruitbowl.push(new Fruit(f.name, f.color, f.seeds)) 
     }) 

Я не исследовал цикл над объектами, как они и приложив все методы. Будет ли это работать?

+0

Для дополнительного кредита: в какой-то момент мне может потребоваться определить мои фрукты и семена с помощью «instanceof». –

ответ

2

Передайте данные в конструктор объекта затем использовать JQuery в «продлить», чтобы объединить данные и методы:

function Fruit(data){ 
    $.extend(this, data) 
    this.eat = function(){ 
     // munch munch 
    } 
} 
... 
     $.each(data.fruitbowl, function(i, f){ 
      fruitbowl.push(new Fruit(f)) 
     }) 

У вас еще есть петли участвуют; и должен вручную закодировать петли для вложенных объектов (например, семена), но все же очень простой способ преодолеть проблему.

2

Да, это сработает, но это нежелательно. Помимо появления слегка взломанной ИМО, вы прилагаете методы к каждому экземпляру ваших фруктов и семян, где вместо этого вы должны использовать цепочку прототипов. Если вы собираетесь использовать instanceof в будущем, этот метод не будет работать.

Что вы сейчас делаете, это лучшее решение; и вы сможете использовать instanceof.

Если вы чувствуете приключений, вы можете использовать JSONP вместо AJAX, с ответом JSONP ищет что-то вроде:

buildFruitbowl([new Fruit("orange", "blue", [new Seed("small", "hard"), new Seed("big", "soft")]), new Fruit("banana", "yellow", [new Seed("small", "hard"), new Seed("big", "soft")])]); 

Который избавит вас от необходимости делать все ваш объект зацикливание, и вы будете получить свои фрукты и семена, как вы хотите (и instanceof); однако я все равно буду придерживаться того, что вы делаете.

Лучшее из ваших растущих бананов.

+2

«Лучше всего выглядеть, растут ваши бананы» - никогда не слышал этого раньше. – Anurag

1

Используя библиотеку «json2» от D Crockford, вы можете предоставить функцию «reviver» для процесса разбора. Функция reviver передается каждой клавише и каждому значению и должна возвращать фактическое эффективное значение, которое будет использоваться в анализируемом результате.

В методе «stringify» имеется соответствующий необязательный параметр.

+1

предупреждение: всякий раз, когда вы решите использовать reviver, вам нужно заставить перезаписать mozilla/firefox <3.6 родной JSON с json2.js. В рецензировании есть документированная ошибка. –

2

Вы можете изменить структуру JSON для хранения информации о типе. Если у вас есть много объектов для сериализации и десериализации взад и вперед, это позволит сэкономить время, набирая собственный код для каждого объекта.

Также обратите внимание, что это изменяет структуру JSON и добавляет свойство __type__ для каждого настраиваемого объекта. Я думаю, что это более чистый подход, чем сохранение отдельных файлов конфигурации. Итак, без дальнейших церемоний, вот как это в основном работает:

var fruitBowl = {..}; 
fruitBowl[0].eat(); 
fruitBowl[1].seeds[0].plant(); 

вызов сериализации на объект, чтобы получить JSON Представительские

var json = fruitBowl.serialize(); 

вызова десериализации на JSON закодированной строки для восстановления объектов

var resurrected = json.deserialize(); 

Теперь вы можете получить доступ к свойствам и вызовам по объектам:

resurrected[0].eat(); 
resurrected[1].seeds[0].plant(); 

Он работает для любых уровней глубоко вложенных объектов, хотя на данный момент это может быть немного ошибкой. Кроме того, это скорее всего не кросс-браузер (проверяется только на Chrome). Поскольку десериализатор не знаком с конструкторской функцией объекта, он в основном создает каждый пользовательский объект без передачи каких-либо параметров. Я установил рабочую демонстрацию на jsfiddle на http://jsfiddle.net/kSATj/1/.

Функция конструктора должен быть изменен, чтобы объяснить двумя способами это объекты могут быть созданы

  1. непосредственно в Javascript
  2. Реконструированный из JSON

Все конструкторы должны были бы приспособить создание с обоих концов, поэтому каждому свойству должно быть присвоено значение резервного значения по умолчанию, если ничего не было передано.

function SomeObject(a, b) { 
    this.a = a || false; // defaultValue can be anything 
    this.b = b || null; // defaultValue can be anything 
} 

// one type of initialization that you can use in your code 
var o = new SomeObject("hello", "world"); 

// another type of initialization used by the deserializer 
var o = new SomeObject();; 
o.a = "hello"; 
o.b = "world"; 

Для справки, модифицированное JSON выглядит следующим образом:

{"fruitbowl": 
    [ 
     { 
      "__type__": "Fruit", 
      "name": "apple", 
      "color": "red", 
      "seeds": []   
     }, 
     { 
      "__type__": "Fruit", 
      "name": "orange", 
      "color": "orange", 
      "seeds": 
      [ 
       { 
        "__type__": "Seed", 
        "size": "small", 
        "density": "hard" 
       }, 
       { 
        "__type__": "Seed", 
        "size": "small", 
        "density": "soft" 
       } 
      ] 
     } 
    ] 
} 

Это просто вспомогательная функция для идентификации простых типов:

function isNative(object) { 
    if(object == null) { 
     return true; 
    } 

    var natives = [Boolean, Date, Number, String, Object, Function]; 
    return natives.indexOf(object.constructor) !== -1; 
} 

сериализацию объекта в JSON (с типом информации консервы):

Object.prototype.serialize = function() { 
    var injectTypes = function(object) { 
     if(!isNative(object)) { 
      object.__type__ = object.constructor.name; 
     } 

     for(key in object) { 
      var property = object[key]; 
      if(object.hasOwnProperty(key) && !isNative(property)) { 
       injectTypes(property); 
      } 
     } 
    }; 

    var removeTypes = function(object) { 
     if(object.__type) { 
      delete object.__type__; 
     } 
     for(key in object) { 
      var property = object[key]; 
      if(object.hasOwnProperty(key) && !isNative(property)) { 
       removeTypes(property); 
      } 
     } 
    } 

    injectTypes(this); 
    var json = JSON.stringify(this); 
    removeTypes(this); 

    return json; 
}; 

Deserialize (с пользовательскими объектами реконструированных):

String.prototype.deserialize = function() { 
    var rawObject = JSON.parse(this.toString()); 

    var reconstruct = function(object) { 
     var reconstructed = {}; 

     if(object.__type__) { 
      reconstructed = new window[object.__type__](); 
      delete object.__type__; 
     } 
     else if(isNative(object)) { 
      return object; 
     } 

     for(key in object) { 
      var property = object[key]; 

      if(object.hasOwnProperty(key)) { 
       reconstructed[key] = reconstruct(property); 
      } 
     } 

     return reconstructed; 
    } 

    return reconstruct(rawObject); 
}; 
+0

Ничего себе, полная оценка за усилия! Немного для облегчения. Отмечается идея перехода __type__. –

+0

Согласен, это не очень полезно, если у вас мало объектов. В этом случае создание объектов с помощью JSON - это, безусловно, лучший и простой подход.Я думаю, что я уже упомянул о недостатках с моим решением - * buggy *, * not cross-browser * и * загрязняет JSON *, но при этом немного больше усилий этот подход должен начать окупаться, поскольку количество объектов, которые необходимо реконструироваться идет вверх. – Anurag

0

Иоанна,

Надеюсь, не слишком поздно, чтобы чип здесь. На прошлой неделе у меня была очень похожая проблема, и я решил ее с помощью следующего фрагмента js (его можно было легко преобразовать в jquery).

Вот основа использования:

$(document).ready(function() { 
     var bowl = { "fruitbowl": [{ 
      "name": "apple", 
      "color": "red", 
      "seeds": [] 
     }, 
     { 
      "name": "orange", 
      "color": "orange", 
      "seeds": [ 
      { "size": "small", "density": "hard" }, 
      { "size": "small", "density": "soft"}] 
     } 
     ] 
     }; 

     var serialized = jsonToObject.serialize(bowl); 
     var deserialized = jsonToObject.deserialize(serialized); 
     // basic tests on serialize/deserializing... 
     alert(deserialized.fruitbowl[0].name); 
     alert(deserialized.fruitbowl[1].seeds[0].density); 
    }); 

и вот файл jsonToObject.js:

jsonToObject = { 

    deserialize: function(_obj) { 
     if (typeof (JSON) === 'object' && typeof (JSON.parse) === 'function') { 
      // native JSON parsing is available. 
      //return JSON.parse(_obj); 
     } 
     // otherwise, try non-native methods 
     var jsonValue = new Function("return " + _obj)(); 

     if (!jsonValue instanceof Object) { 
      jsonValue = eval("(" + _obj + ")"); 
     } 
     return jsonValue; 
    }, 

    serialize: function(_obj) { 
     // Let Gecko browsers do this the easy way - not working 
     if (_obj != undefined && typeof _obj.toSource !== 'undefined' 
      && typeof _obj.callee === 'undefined') { 
      return _obj.toSource(); 
     } 

     // Other browsers must do it the hard way 
     switch (typeof _obj) { 
      // numbers, booleans, and functions are trivial:     
      // just return the object itself since its default .toString()     
      // gives us exactly what we want     
      case 'number': 
      case 'boolean': 
      case 'function': 
       return _obj; 
       break; 

      // for JSON format, strings need to be wrapped in quotes     
      case 'string': 
       return '"' + _obj.replace(/"/mg, "'") + '"'; 
       break; 

      case 'object': 
       var str; 
       if (_obj.constructor === Array || typeof _obj.callee !== 'undefined') { 
        str = '['; 
        var i, len = _obj.length; 
        for (i = 0; i < len - 1; i++) { str += this.serialize(_obj[i]) + ','; } 
        str += this.serialize(_obj[i]) + ']'; 
       } 
       else { 
        str = '{'; 
        var key; 
        for (key in _obj) { str += key + ':' + this.serialize(_obj[key]) + ','; } 
        str = str.replace(/\,$/, '') + '}'; 
       } 
       return str; 
       break; 

      default: 
       return '""'; 
       break; 
     } 
    } 
} 

надеюсь, что это помогает ...

джим

[править] - Вы, конечно, может также дать две функции свои подписи прототипа в соответствии с отличным примером выше, то есть ..

String.prototype.deserialize = функция () {...} Object.prototype.serialize = function() {...}

+0

спасибо за идею. deserialize всегда выполняется на строках, поэтому в идеале это должно быть добавлено к прототипу String. – Anurag

+0

да, я сделал локальную версию, которая использовала прототипы sigs, но оставила мой пример, как указано, что ваш использует этот подход - дает немного разнообразия :) Кстати, вам все равно нужно обновить ссылку на строку в вашем примере (http://jsfiddle.net/kSATj/) - на всякий случай, если вы его не заметите;) –

+0

Разнообразие всегда хорошее :) и спасибо за подсказку с цепочкой. Просто обновил мой пример – Anurag

1

Это на самом деле заняло некоторое время, чтобы понять, я действительно удивлен, что на нем больше нет страниц.

Как отметил @Pointy, JSON имеет функцию reviver, которая может быть использована для замены встроенного синтаксического анализа, позволяющего избежать ходьбы от дерева во второй раз. Документы страницы JSON reviver (по-моему немного слабые) - http://json.org/js.html.

Reviver является частью ECMA 5 и поддерживается в Firefox, WebKit (Opera/Chrome) и JSON2.js.

Вот пример кода, основанный на документе JSON. Вы можете видеть, что мы устанавливаем свойство type на Dog, а затем используем функцию reviver, которая распознает свойство type.

function Dog(args) { 
    this.name = args.name; 
    this.bark = function() { 
     return "bark, bark, my name is " + this.name; 
    }; 
    this.toJSON = function() { 
     return { 
      name: this.name, 
      type: 'Dog' // this.constructor.name will work in certain browsers/cases 
     } 
    } 
}; 

var d = new Dog({name:'geti'}); 

var dAsJson = JSON.stringify(d); 
var dFromJson = JSON.parse(dAsJson, function (key, value) { 
    var type; 
    if (value && typeof value === 'object') { 
     type = value.type; 
     if (typeof type === 'string' && typeof window[type] === 'function') { 
      return new (window[type])(value); 
     } 
    } 
    return value; 
} 
); 

У меня есть пара вопросов относительно их примера. Во-первых, это зависит от того, что конструктор является глобальным (в окне). Во-вторых, проблема безопасности в этом жулике JSON может заставить нас вызвать любой конструктор, добавив свойство типа в их JSON.

Я выбрал явный список типов и их конструкторов. Это гарантирует, что только конструкторы, которые, как я знаю, будут безопасными, будут вызваны, а также позволят мне использовать подход сопоставления пользовательского типа, если мне нравится (а не зависит от имени конструктора и его нахождения в глобальном пространстве). Я также проверяю, что объект JSON имеет тип (некоторые могут и не будут обрабатываться нормально).

var jsonReviverTypes = { 
    Dog: Dog 
}; 

var dAsJsonB = JSON.stringify(d); 
var dFromJsonB = JSON.parse(dAsJsonB, function (key, value) { 
    var type; 
    if (value && typeof value === 'object' && value.type) { 
     type = value.type; 
     if (typeof type === 'string' && jsonReviverTypes[type]) { 
      return new (jsonReviverTypes[type])(value); 
     } 
    } 
    return value; 
}); 

Примечание, FF 3.6 есть ошибка в методе JSON.replacer, как @Sky указал, и описываемая здесь - http://skysanders.net/subtext/archive/2010/02/24/confirmed-bug-in-firefox-3.6-native-json-implementation.aspx. Для вышеупомянутого решения я обхожу это, используя toJSON на объекте, а не используя replacer.

2

Использование ES5 Object.create

Просто определите ваши объекты статически затем использовать Object.create расширить их.

Это так просто, как Object.create(Bowl, transform(data));

// declare 3 Objects to use as prototypes for your data 
var Fruit = { 
    eat: function() { } 
} 

var Seed = { 
    plant: function() { } 
} 

var Bowl = {}; 

// data object 
var data = { ... }; 


// Transform JSON to a valid defineProperties hash. 
Object.create(Bowl, transform(data)); 

Вам нужно будет определить функцию преобразования и, что более важно сказать это тип объекта вложенных массивов данных.

// hash map of property names of arrays to the Object they should prototype from. 
var collectionClassHash = { 
    fruitbowl: Fruit, 
    seeds: Seed 
} 

var transform = function(obj) { 
    // return value 
    var ret = {}; 
    // for each key 
    Object.keys(obj).forEach(function(key) { 
    // value of key 
    var temp = obj[key]; 
    // if array 
    if (Array.isArray(temp) { 
     // override value with an array of the correct objects 
     temp = obj[key].map(function(val) { 
     // recurse for nested objects 
     return Object.create(collectionClassHash[key], transform(val)); 
     }); 
    } 
    // define getter/setter for value 
    ret[key] = { 
     get: function() { return temp; }, 
     set: function(v) { temp = v; } 
    } 
    }); 
    return ret; 
} 
Смежные вопросы