Highly Nested JSON Payload - hasMany error


#1

Hi there, (this post is a duplicate to: https://github.com/emberjs/data/issues/3710 but my problem is yet not solved…) so i hope i can get your help now… :smile:

i’m trying to migrate my Ember 1.12.x App to Ember 1.13.8 and im going crazy with our JSON and Ember Data. I really need help, i tryed out so many things and no progress :frowning:

I have Nested JSON Payload like this:

{  
"dashboard":[  
  {  
     "elementBeanList":[  
        {  
           "fieldBeanList":[  
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.Integer"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              }
           ],
           "elementClassId":"xxx",
           "elementBeanList":[  

           ],
           "count":null,
           "link":"5",
           "linkList":[  
              {  
                 "id":"xxx",
                 "value":"xxx"
              }
           ]
        },
        {  
           "fieldBeanList":[  
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.Integer"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              }
           ],
           "elementClassId":"xxx",
           "elementBeanList":[  

           ],
           "count":null,
           "link":"1",
           "linkList":[  
              {  
                 "id":"xxx",
                 "value":"xxx"
              },
              {  
                 "id":"xxx",
                 "value":"xxx"
              }
           ]
        },
        {  
           "fieldBeanList":[  
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.Integer"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              },
              {  
                 "fieldName":null,
                 "fieldType":"java.lang.String"
              }
           ],
           "elementClassId":"xxx",
           "elementBeanList":[  

           ],
           "count":null,
           "link":"2",
           "linkList":[  
              {  
                 "id":"xxx",
                 "value":"xxx"
              },
              {  
                 "id":"xxx",
                 "value":"xxx"
              }
           ]
        }
     ]
  }
],
"titel":"Dashboard"

}

And my models:

dashboard:

export default DS.Model.extend({
elementBeanList: DS.hasMany('elementbeanlist')
});

elementbeanlist:

export default DS.Model.extend({
fieldBeanList: DS.hasMany('fieldbeanlist'),
elementBeanList: DS.hasMany('elementbeanlist', {inverse: null}),
elementClassId: DS.attr('string'),
link: DS.attr('string'),
linkList: DS.attr(),
count: DS.attr(),
});

fieldbeanlist:

export default DS.Model.extend({
fieldName:DS.attr('string'),
fieldType:DS.attr('string')
});

And i modified the serializer:

dashboardSerializer:

export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin,{
attrs: {
    elementBeanList: { embedded: 'always' },
    fieldbeanlist: { embedded: 'always' },
},
normalizePayload: function(payload) {
    console.log("DashboardSerializer > normalizePayload");

    if(payload.elementBeanWrapper){
      payload.dashboard = payload.elementBeanWrapper;
      delete payload.elementBeanWrapper;
        }
     return payload;
 }
});

When i try transition to the Dashboard Route i get this error and dont know how to solve it!

The Error is this:

Ember Data expected a number or string to represent the record(s) in the fieldBeanList relationship instead it found an object. If this is a polymorphic relationship please specify a type key. If this is an embedded relationship please include the DS.EmbeddedRecordsMixin and specify the fieldBeanList property in your serializer’s attrs object.

And

Error: Assertion Failed: Ember Data expected a number or string to represent the record(s) in the fieldBeanList relationship instead it found an object. If this is a polymorphic relationship please specify a type key. If this is an embedded relationship please include the DS.EmbeddedRecordsMixin and specify the fieldBeanList property in your serializer’s attrs object.

So did the following changes:

export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin,{
primaryKey: 'elementClassId',
attrs: {
    elementBeanList: { embedded: 'always' },
    fieldbeanlist: { embedded: 'always' },
},

but the error is still there… Please tell me if you need more informations or something else!

Thanks in advance &best regards rocki


#2

Sorry if this is not a very useful answer but… if you’re using Ember Data 1.13 why bother with RESTSerializer and EmbeddedRecordsMixin?

I would go the way forward using a custom JSONAPISerializer where you massage your data to fit into Ember Data’s native data model in 1.13, JSON API. I would find that easier to debug.


#3

hey igniter,

thanks for your reply. Do you have some suggestions for me how to modify my data in the JSONAPISerializer?

The documentation is not clear for me and the given data model from my server m8s…

Which Steps do i have to do? Which Hooks/Method should i overwrite?


#4

You should override the normalizeResponse hook. Have a look at this section: “NEW SERIALIZER API FOR CUSTOM SERIALIZER AUTHORS”

You can also check out this answer I gave on Stack Overflow for a similar question:

Remember you should DS.JSONAPISerializer.extend({ ... }).


#5

Thanks!

I’ll try my best :smile:

best regards, rocki


#6

Rockinho did you manage to make your data work?

I recently wrote something about adapters and serializers, partly based on your issue:

http://emberigniter.com/fit-any-backend-into-ember-custom-adapters-serializers/

Hope that helps somewhat!


#8

Hey Frank,

i like your article!

But can you please integrate an example of embedded hasMany relationships? And what can i do when my api doesnt return suitable IDs for my records? And do you have an advice for me how to debug: I tried to override the JSONAPISerializer like this (for the given JSON)

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend({
normalizeResponse(store, type, payload) {
	payload.dashboard = payload.elementBeanWrapper;
	delete payload.elementBeanWrapper;
	console.log("Payload: ", payload);
	console.log("payload.elementBeanList: ", payload.dashboard);

  return {
    data: {
      id: payload.titel,
      type: type.modelName,
      attributes: {
		  elementBeanList: payload.elementBeanList
      }
    }
  }
},
});

In the ember store a “dashboard” item was created but its not filled with data… -.-

“canonical state” is empty…


#9

Hey, I will consider adding the embedded example. I am not sure what your payload looks like so it’s difficult to see what is going wrong.

I would inspect the JSON API structure (ie data: {}) in a variable before returning:

var result = { data: { ... } }
console.log(result);
return result;

so I can then check if it’s valid JSON API. For that you can refer to http://jsonapi.org/format/ !

If you’d like to include embedded resources, simply add them like this (besides data):

{ 
  data: {},
  included: [{ 
    id: '1',
    type: 'type',
    attributes: {}
  }]
}

#10

@rockinho fieldbeanlist != fieldBeanList

(In dashboardSerializer.attrs key)


#11

Hello @emberigniter ,

i checked my payload on: https://jsonformatter.curiousconcept.com/ and it seems to be valid i would really appreciate if you / someone could help me. because when i once managed to get our data structure into the ember store i can work further… until then i do not get ahead :frowning:

Here it is:

{
"meta":null,
"elementBeanWrapper":[
  {
     "elementBeanList":[
        {
           "fieldBeanList":[
              {
                 "fieldName":null,
                 "fieldType":"Integer",
                 "fieldValue":"5",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"id",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"Archiv",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"name",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"BA",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"kurzname",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"Belege",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"beschreibung",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              }
           ],
           "elementClassId":"Modul",
           "elementBeanList":[

           ],
           "count":null,
           "itemId":null,
           "link":"5",
           "linkList":[
              {
                 "id":"Search",
                 "value":"/5"
              }
           ]
        },
        {
           "fieldBeanList":[
              {
                 "fieldName":null,
                 "fieldType":"Integer",
                 "fieldValue":"1",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"id",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"Value",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"name",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"Valoue2",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"kurzname",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"Docs",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"beschreibung",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              }
           ],
           "elementClassId":"Modul",
           "elementBeanList":[

           ],
           "count":null,
           "itemId":null,
           "link":"1",
           "linkList":[
              {
                 "id":"Search",
                 "value":"/1"
              },
              {
                 "id":"Search2",
                 "value":"/1"
              }
           ]
        },
        {
           "fieldBeanList":[
              {
                 "fieldName":null,
                 "fieldType":"Integer",
                 "fieldValue":"2",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"id",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"ValuePA",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"name",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"ValuePa",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"kurzname",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              },
              {
                 "fieldName":null,
                 "fieldType":"String",
                 "fieldValue":"Doks",
                 "fieldValueId":null,
                 "valueBeanList":null,
                 "fieldDescription":null,
                 "fieldId":"beschreibung",
                 "fieldValidation":null,
                 "fieldMandantory":false,
                 "fieldCrypted":false,
                 "fieldFormat":null,
                 "linkList":null
              }
           ],
           "elementClassId":"Modul",
           "elementBeanList":[

           ],
           "count":null,
           "itemId":null,
           "link":"2",
           "linkList":[
              {
                 "id":"search",
                 "value":"/2"
              },
              {
                 "id":"search2",
                 "value":"/2"
              }
           ]
        }
     ]
  }
],
"titel":"Dashboard"
}

And this is my Serializer:

import DS from 'ember-data';

export default DS.JSONAPISerializer.extend({
normalizeResponse(store, type, payload) {
	payload.dashboard = payload.elementBeanWrapper;
	delete payload.elementBeanWrapper;
	console.log("Payload: ", payload);
	console.log("payload.elementBeanList: ", payload.dashboard);

  return {
    data: {
      id: payload.titel,
      type: type.modelName,
      attributes: {
		  elementBeanList: payload.elementBeanList
      }
    }
  }
 },
});

And my Model Structure:

dashboard

import DS from "ember-data";

export default DS.Model.extend({
    elementBeanList: DS.hasMany('elementbeanlist')
});

elementbeanlist

import DS from "ember-data";

export default DS.Model.extend({
    fieldBeanList: DS.hasMany('fieldbeanlist', { embedded: 'always' }),
    elementBeanList: DS.hasMany('elementbeanlist', {inverse: null}),
    elementClassId: DS.attr('string'),
    link: DS.attr('string'),
    linkList: DS.attr(),
    count: DS.attr(),
});

fieldbeanlist

import DS from “ember-data”;

export default DS.Model.extend({
    elementBeanList: DS.belongsTo('elementbeanlist'),
    fieldCrypted:DS.attr('boolean'),
    fieldDescription:DS.attr('string'),
    fieldFormat: DS.attr('string'),
    fieldId: DS.attr('string'),
    fieldMandantory: DS.attr('boolean'),
    fieldName: DS.attr('string'),
    fieldType: DS.attr('string'),
    fieldValidation: DS.attr('string'),
    fieldValue: DS.attr(),
    fieldValueId: DS.attr('string'),
    linkList:DS.attr(),
    valueBeanList: DS.attr(),
    fromToDate: function(){
      return this.get('fieldValue')+"V"+this.get('valueBeanList')
 }.property('fieldValue','valueBeanList')
});

Best regards,

rocki


#12

@rockinho Did you correct the error in dashboardSerializer that i mentioned?


#13

hey!

yes i tried your mentioned solution but it does not work at all. i hope that @emberigniter can find the time to help me, becaus i believe his solution is more state-of-the-art.

best regards rocki


#14

OK. With the code the way it is now, what do you get? Anything being logged?

Note that you have another typo in the JSON: titel => title. Although i’m not sure it affects anything.


#15

Rocki, yea, sorry, I don’t have much time these days.

You didn’t post how the transformed JSON API looks like, i.e. whatever you return in the serializer. It appears that you were validating your JSON code? That’s fine, but we’re talking about making it JSON API compliant! (Subtle but important difference! You should check out jsonapi.org/format)

Something you could find beneficial is making a simpler example of your code so can people here in the forum can understand it better.