序列化,将实体转换为普通对象


95

我对javascript并不太熟悉,而且令人叹为观止,因为我无法向使用ORM名称Sequelize.js从数据库中获取的对象添加新属性。

为了避免这种情况,我使用此技巧:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success(function (sensors) {
        var nodedata = JSON.parse(JSON.stringify(node)); // this is my trick
        nodedata.sensors = sensors;
        nodesensors.push(nodedata);
        response.json(nodesensors);
});

因此,通常如何向对象添加新属性。

如果有帮助,我使用sequelize-postgres版本2.0.x。

更新。console.log(节点):

{ dataValues: 
   { nodeid: 'NodeId',
     name: 'NameHere',
     altname: 'Test9',
     longname: '',
     latitude: 30,
     longitude: -10,
     networkid: 'NetworkId',
     farmid: '5',
     lastheard: Mon Dec 09 2013 04:04:40 GMT+0300 (FET),
     id: 9,
     createdAt: Tue Dec 03 2013 01:29:09 GMT+0300 (FET),
     updatedAt: Sun Feb 23 2014 01:07:14 GMT+0300 (FET) },
  __options: 
   { timestamps: true,
     createdAt: 'createdAt',
     updatedAt: 'updatedAt',
     deletedAt: 'deletedAt',
     touchedAt: 'touchedAt',
     instanceMethods: {},
     classMethods: {},
     validate: {},
     freezeTableName: false,
     underscored: false,
     syncOnAssociation: true,
     paranoid: false,
     whereCollection: { farmid: 5, networkid: 'NetworkId' },
     schema: null,
     schemaDelimiter: '',
     language: 'en',
     defaultScope: null,
     scopes: null,
     hooks: { beforeCreate: [], afterCreate: [] },
     omitNull: false,
     hasPrimaryKeys: false },
  hasPrimaryKeys: false,
  selectedValues: 
   { nodeid: 'NodeId',
     name: 'NameHere',
     longname: '',
     latitude: 30,
     longitude: -110,
     networkid: 'NetworkId',
     farmid: '5',
     lastheard: Mon Dec 09 2013 04:04:40 GMT+0300 (FET),
     id: 9,
     createdAt: Tue Dec 03 2013 01:29:09 GMT+0300 (FET),
     updatedAt: Sun Feb 23 2014 01:07:14 GMT+0300 (FET),
     altname: 'Test9' },
  __eagerlyLoadedAssociations: [],
  isDirty: false,
  isNewRecord: false,
  daoFactoryName: 'Nodes',
  daoFactory: 
   { options: 
      { timestamps: true,
        createdAt: 'createdAt',
        updatedAt: 'updatedAt',
        deletedAt: 'deletedAt',
        touchedAt: 'touchedAt',
        instanceMethods: {},
        classMethods: {},
        validate: {},
        freezeTableName: false,
        underscored: false,
        syncOnAssociation: true,
        paranoid: false,
        whereCollection: [Object],
        schema: null,
        schemaDelimiter: '',
        language: 'en',
        defaultScope: null,
        scopes: null,
        hooks: [Object],
        omitNull: false,
        hasPrimaryKeys: false },
     name: 'Nodes',
     tableName: 'Nodes',
     rawAttributes: 
      { nodeid: [Object],
        name: [Object],
        altname: [Object],
        longname: [Object],
        latitude: [Object],
        longitude: [Object],
        networkid: [Object],
        farmid: [Object],
        lastheard: [Object],
        id: [Object],
        createdAt: [Object],
        updatedAt: [Object] },
     daoFactoryManager: { daos: [Object], sequelize: [Object] },
     associations: {},
     scopeObj: {},
     primaryKeys: {},
     primaryKeyCount: 0,
     hasPrimaryKeys: false,
     autoIncrementField: 'id',
     DAO: { [Function] super_: [Function] } } }

接下来,我想您会想到的是:“好吧,很简单,只需将您的属性添加到dataValues中即可。”

node.selectedValues.sensors = sensors;
node.dataValues.sensors = sensors;

我添加了这行,这行不通


node一定不能成为传统对象。也许是缓冲?console.log(node);在您的技巧专线之前怎么说?
凯文·赖利2014年

请参阅更新。
Ruslan 2014年

Answers:


42

如果我说对了,您要将sensors集合添加到中node。如果两个模型之间都有映射,则可以使用此处说明include功能或在每个实例上定义的getter。您可以在此处找到相关文档。values

后者可以这样使用:

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  }
}).success(function (sensors) {
  var nodedata = node.values;

  nodedata.sensors = sensors.map(function(sensor){ return sensor.values });
  // or
  nodedata.sensors = sensors.map(function(sensor){ return sensor.toJSON() });

  nodesensors.push(nodedata);
  response.json(nodesensors);
});

有机会也有nodedata.sensors = sensors可能。


2
谢谢,我只需要:node.values :)
Ruslan

147

您可以使用查询选项{raw: true}返回原始结果。您的查询应如下所示:

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  },
  raw: true,
})

如果您与之的关联include也变扁平了。因此,我们可以使用另一个参数nest:true

db.Sensors.findAll({
  where: {
    nodeid: node.nodeid
  },
  raw: true,
  nest: true,
})

32
“ raw:true”将以某种方式展平关联模型注入的数组。到目前为止,我发现的唯一解决方法是configs = JSON.parse(JSON.stringify(configs));
林一速(Soichi Hayashi)

1
这是比公认的答案更简单,更好的方法。谢谢
pyprism

1
@SoichiHayashi实际上,当您考虑它时,它的意思是相反的...;)没有raw:是的,Sequelize以某种方式将结果展平为对象。sql查询的结果已经被拉平。我发现几次有用以获得平坦的结果……为此答案+1。
PRS

4
@SoichiHayashi可以获取原始但嵌套的结果,可以将其nest: true与一起使用raw: true
1valdis

任何想法如何将传递raw:true为全局设置,所以没有必要将其传递给每个查询?谢谢
Codebot

109

对于最近遇到此问题的人员,.values自Sequelize 3.0.0起不推荐使用。使用.get(),而不是拿到普通的JavaScript对象。因此,以上代码将更改为:

var nodedata = node.get({ plain: true });

在此处排列文档


6
如果我错了,请纠正我-但是我认为这不适用于行数组吗?例如,使用.findAndcount时,您必须.map结果行,并在每行上返回.get以获得所需的内容。
后门服务'16

仅运行JSON.stringify或res.json可能更容易(如果您在Express中工作)。
后门服务'16

@backdesk-尚未在数组上尝试过-但从文档看来,它应该返回相同的内容。
CharlesA

3
使用.get()不会转换包含的关联,请.get({ plain: true })改为使用。感谢您提到文档中的建议。
Wumms

如果您已经执行了查询因而无法发出查询,也将非常有用raw: true
Matt Molnar

49

最好的简单方法是:

只需使用Sequelize中的默认方式

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    },
    raw : true // <----------- Magic is here
}).success(function (sensors) {
        console.log(sensors);
});

注意:[options.raw]:返回原始结果。有关更多信息,请参见sequelize.query。


对于嵌套结果/如果我们包含模型,则在最新版本的sequlize中,

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    },
    include : [
        { model : someModel }
    ]
    raw : true , // <----------- Magic is here
    nest : true // <----------- Magic is here
}).success(function (sensors) {
        console.log(sensors);
});

这个答案非常有用,使用sequleize提供的本机功能,在我的情况下,当我在套接字中发送多个行时,发生了堆栈溢出错误。
Ashish Kadam19年

2
如果您有一系列子数据,则将得到错误的结果。所以,如果贴查询返回只有一个sensors有孩子的数组someModel,你会得到一个arraysensors一个someModel在每个。
Rahmat Ali

31

正如CharlesA在其答案中指出的那样,虽然docs中并未明确指出这一事实,但从技术上讲.values()已过时。如果您不想在查询中使用,则首选方法是调用结果。{ raw: true }.get()

.get()但是,它是实例的方法,而不是数组的方法。如上面链接的问题所述,Sequelize返回实例对象的本机数组(并且维护者不打算更改该对象),因此您必须自己遍历该数组:

db.Sensors.findAll({
    where: {
        nodeid: node.nodeid
    }
}).success((sensors) => {
    const nodeData = sensors.map((node) => node.get({ plain: true }));
});

这对我有用!虽然我不知道'get'是JavaScript还是Sequelize函数。请赐教。谢谢
antew

1
@antew这是从数据库返回的对象上的方法-它是sequelize库的一部分,而不是本机javascript函数。
Shane Hughes

花了我很长时间才能完成这项工作!谢谢。太难了,因为我有很多嵌套的包含,这也是一个错误。所以我必须做多个查询,而我不能因为这个!!!
卡尔·泰勒

17

您可以使用地图功能。这对我有用。

db.Sensors
    .findAll({
        where: { nodeid: node.nodeid }
     })
    .map(el => el.get({ plain: true }))
    .then((rows)=>{
        response.json( rows )
     });

谢谢,这是我使用的最佳解决方案,只是results = results.map(el => el.get({ plain: true }))
Joshua Ohana

9

我找到了一种使用本机JavaScript函数对嵌套模型和数组运行良好的解决方案。

var results = [{},{},...]; //your result data returned from sequelize query
var jsonString = JSON.stringify(results); //convert to string to remove the sequelize specific meta data

var obj = JSON.parse(jsonString); //to make plain json
// do whatever you want to do with obj as plain json

1
如此简单,但功能强大,并保持了模型逻辑。我不知道它的效率如何,但是对我来说已经足够了。您可以将其缩写为:let res = JSON.parse(JSON.stringify(result));
Artipixel

9

这就是我用来从sequelizev4查询获取具有非字符串化值和所有嵌套关联的普通响应对象的方法。

使用纯JavaScript(ES2015 +):

const toPlain = response => {
  const flattenDataValues = ({ dataValues }) => {
    const flattenedObject = {};

    Object.keys(dataValues).forEach(key => {
      const dataValue = dataValues[key];

      if (
        Array.isArray(dataValue) &&
        dataValue[0] &&
        dataValue[0].dataValues &&
        typeof dataValue[0].dataValues === 'object'
      ) {
        flattenedObject[key] = dataValues[key].map(flattenDataValues);
      } else if (dataValue && dataValue.dataValues && typeof dataValue.dataValues === 'object') {
        flattenedObject[key] = flattenDataValues(dataValues[key]);
      } else {
        flattenedObject[key] = dataValues[key];
      }
    });

    return flattenedObject;
  };

  return Array.isArray(response) ? response.map(flattenDataValues) : flattenDataValues(response);
};

使用lodash(更加简洁):

const toPlain = response => {
  const flattenDataValues = ({ dataValues }) =>
    _.mapValues(dataValues, value => (
      _.isArray(value) && _.isObject(value[0]) && _.isObject(value[0].dataValues)
        ? _.map(value, flattenDataValues)
        : _.isObject(value) && _.isObject(value.dataValues)
          ? flattenDataValues(value)
          : value
    ));

  return _.isArray(response) ? _.map(response, flattenDataValues) : flattenDataValues(response);
};

用法

const res = await User.findAll({
  include: [{
    model: Company,
    as: 'companies',
    include: [{
      model: Member,
      as: 'member',
    }],
  }],
});

const plain = toPlain(res);

// 'plain' now contains simple db object without any getters/setters with following structure:
// [{
//   id: 123,
//   name: 'John',
//   companies: [{
//     id: 234,
//     name: 'Google',
//     members: [{
//       id: 345,
//       name: 'Paul',
//     }]
//   }]
// }]

1
谢谢!toPlain可以正常工作,也解决了我的问题。
Erhnam

这很简洁,但是如果您覆盖模型的toJSON方法中的任何一个,那就不好了。为了删除或转换createdAt / updatedAt值,我做了很多工作。到目前为止,对我而言唯一正常工作的是JSON.parse(JSON.stringify(response))。
hamham

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.