Formatting JSON using the RESTSerializer in the latest Ember Data version - javascript

I'm struggling to 'munge' my JSON into the correct format.
To illustrate i've made a quick, JSfiddle.
http://jsfiddle.net/chrismasters/NQKvy/638/
The format the server returns the data has a couple of differences to the preferred format recommended by Ember Data now.
Here is the raw JSON output
{
"video": {
"uuid": "8a660002-03c6-4b8e-bd8b-4ce28fa0dacd",
"state": "pending",
"theme": "basic",
"resolution": "nHD",
"title": "Test title",
"track": {
"uuid": "376fc3bb-d703-49e7-9d92-bce7f6bf8b56",
"state": "complete",
"source": "upload"
}
}
}
The first is that rather than use IDs it uses a UUID that is a string.
I seem to have managed to fix that using the normalizeHash, for video at least - but i'm not sure whether the same approach will fix the track model too - especially if I use embedding as I need to.
This is where the big problems start to appear, if I comment out the belongsTo relationship from the video model then it works OK, so I think... it is clearly a problem with the JSON formatting for the embedded track data.
Here are the model definitions and the serialization
App.Video = DS.Model.extend({
title: DS.attr('string'),
//track: DS.belongsTo('track', { embedded: true })
});
App.VideoSerializer = DS.RESTSerializer.extend({
normalizeHash: {
video: function(hash) {
hash.id = hash.uuid;
delete hash.uuid;
return hash;
}
}
});
I'd really appreciate some advice on how to format this response into a format that Ember Data recognises.
Also - does anyone know of a tool or good way of debugging these serialization transformations because at the moment the error message from Ember is not very helpful in terms of debugging or seeing what the serialization output is.
Many thanks for any help you can suggest.
Chris

In case anyone else has the same confusion over serializations I thought i'd include an explanation how to solve this problem.
Here is the working jsbin:
http://jsbin.com/fuzu/4
The main points are:
Primary Keys
primaryKey: 'uuid'
Is useful to convert the id into the correct naming & needs to be applied explicitly to any serializers (using globally on a ApplicationSerializer didn't seem to work).
Model Relationships
track: DS.belongsTo('track', {embedded: true} )
Ensure the definition of the relationship includes embedding & only on one side.
Extract Single
extractSingle: function(store, type, payload, id, requestType) {
var tracks = [];
var track = payload.video.track;
var video = payload.video;
tracks.push(track);
video.track = payload.video.track.uuid;
payload = { video: video, track: tracks };
return this._super(store, type, payload, id, requestType);
}
Pluralization is really important for Ember Data to understand the relationships, even though the model relationship is a belongsTo.
You can see this clearly in the desired (working) JSON
{
"video": {
"id": "8a660002-03c6-4b8e-bd8b-4ce28fa0dacd",
"state": "pending",
"theme": "basic",
"resolution": "nHD",
"title": "Test title",
"track": "2"
},
"track": [{
"id": "2",
"state": "complete",
"source": "upload"
}]
}
The track value in video isn't wrapped in an array, yet the root track value is an array.
For this reason I found it very useful first define the desired JSON and test it working first, then try to munge the real JSON into that format.
I think a tool to help with this process (visualising real-time JSON output from seraliziation) could be a great addition to Ember Data & something I'm going to look into creating.

Related

Loopback scope filter does not work in find query

I am trying to load 5 data included to main model. But instead of this, I got whole relation data and so on. The problem caused by include filter
const person = await this.PersonModel.findOne({
"where": {
"id": personId,
"userId": userId,
},
"include": {
"relation": "projects",
"scope": {
"order": "createdDate DESC",
"skip": 0,
"limit": 5
}
}
});
When I use order parameter alone, it works. But when I add skip and limit parameters, order parameter doesn't work either.
So what am I doing wrong?
I found the solution. Loopback3 does not support second level filtering. So this means you are not able to run filter query at included data. You have to create function/endpoint in the end.

JSON attribute unreachable - "Cannot read property of undefined"

I am attempting to use the Wikipedia API to retrieve article titles and snippets of the article's text. But when I try to access those properties, I am getting the error "Cannot read property of undefined."
Here is my JSON response:
{
"batchcomplete": "",
"continue": {
"gsroffset": 10,
"continue": "gsroffset||"
},
"query": {
"pages": {
"13834": {
"pageid": 13834,
"ns": 0,
"title": "\"Hello, World!\" program",
"index": 6,
"extract": "<p>A <b>\"Hello, World!\" program</b> is a computer program that outputs or displays \"Hello, World!\" to a user. Being a very simple program in most programming languages, it is often used to illustrate the</p>..."
},
"6710844": {
"pageid": 6710844,
"ns": 0,
"title": "Hello",
"index": 1,
"extract": "<p><i><b>Hello</b></i> is a salutation or greeting in the English language. It is first attested in writing from 1826.</p>..."
},
"1122016": {
"pageid": 1122016,
"ns": 0,
"title": "Hello! (magazine)",
"index": 7,
"extract": "<p><i><b>Hello</b></i> (stylised as <i><b>HELLO!</b></i>) is a weekly magazine specialising in celebrity news and human-interest stories, published in the United Kingdom since 1988. It is the United Kingdom</p>..."
}
}
}
}
I have tried a couple different ways of writing the code. For example, this works (logs the pages as an object in the console):
console.log(response.query.pages);
But this returns the error I wrote above ("Cannot read property of undefined"):
console.log(response.query.pages[0].title);
Any suggestions on how to access the attributes "title" and "extract" would be appreciated. Thanks.
That's because pages is not an array; it's an object where the keys are the ids. So you need to do:
console.log(response.query.pages[1122016].title);
This will work. If you want the "first" page, for instance, then
let pages = response.query.pages;
console.log(pages[Object.keys(pages)[0]].title);
Note that I'm not sure if the order of the keys in JS objects is guaranteed.
If you want to iterate over the pages, do
let pages = response.query.pages;
Object.keys(pages).forEach(id => {
let page = pages[id];
console.log(page.title, page.foo);
});
Special Case: Working with Asynchronous Calls
Howdy fellow devs,
If you're checking out this thread because you're working with a framework like React, or some other framework that has you using a development server (e.g. using npm start or something similar), your dev server may be crashing when you try to do something like console.log(response.foo.bar) before it refreshes on data reload.
Specifically, my dev server was crashing with the Cannot read property 'bar' of undefined type message, and I was like, "what the heck is going on here!?". Solution: put that baby in a try/catch block:
try {
console.log(rate['bar'].rate)
} catch (error) {
console.log(error)
}
Why? If your App has a default state (even an empty array, for example), then it tries to console.log the response before the data has been received from the remote source, it will successfully log your empty state, but if you try to reference parts of the object you're expecting to receive from the remote source in your console.log or whatever else, the dev server will be trying to reference something not there on initial load and crash before it has a chance to reference it when it's actually received from the remote source via API or whatever.
Hope this helps someone!
I'm not sure which language you're using to parse the JSON (looks like Javascript from console.log?) but the issue is that query.pages is a dictionary, not an array, so it can't be iterated by index, only by key.
So you want something like (pseudocode):
for (key in response.query.keys)
{
console.log(response.query[key].title);
}

"Assertion Failed: The response from a findAll must be an Array, not undefined"

I am trying to make Emberjs work with the fortunejs backend. So far I made a very simple page in Ember that should show all the 'customer' entities, which works fine with the LSadapter.
When my app loads the overview page, its does a GET request to http://localhost:1337/customers and fortune responds with:
{
"customers": [
{
"id": "YIR17juOFkaWBFhl",
"name": "PIm",
"phone": 132,
"fax": 123,
"chamberOfCommerceNumber": 123,
"website": "123.nl"
},
{
"id": "gUGIoHvwI8mwVTgE",
"name": "Marco",
"phone": 123,
"fax": 123,
"chamberOfCommerceNumber": 123,
"website": "it.nl"
}]}
However, ember does not seem to like it and gives me:
"Assertion Failed: The response from a findAll must be an Array, not
undefined"
I thought that this is the right json format ember expects from a get request to get all customers, what is going wrong?
FYI, I made a create page that works fine with fortunejs. For that I had to modify the RESTSerializer like so:
App.ApplicationSerializer = DS.RESTSerializer.extend({
serialize: (record, options) ->
[this._super record, options] #Turn into array
serializeIntoHash: (hash, type, record, options) ->
console.log type.typeKey
type.typeKey = Ember.Inflector.inflector.pluralize type.typeKey #pluralize root key
console.log type.typeKey
console.log record
result = this._super hash, type, record, options
});
But this should have nothing to do with the incoming JSON form the api.
Any ideas what goes wrong?
I know this is old, and I'm not sure Stack Overflow's policy on that but I was trolling through unanswered questions to see if I knew any off the top of my head...
I think the problem here is that findAll (which doesn't exist in the current version of Ember Data) is a method that only does a store lookup. The equivalent of store.all in the current version (http://emberjs.com/api/data/classes/DS.Store.html#method_all).
You would want store.find, which makes a backend server request for the data (http://emberjs.com/api/data/classes/DS.Store.html#method_find).
Hope this helps anyone who stumbles across this problem...

gdata api v3 youtube, can not retrieve contentDetails

I am trying to get the duration of a youtube video via search
var request = gapi.client.youtube.search.list({
q: q,
type : "video",
maxResults : 10,
part: 'snippet,contentDetails'
});
as an additional part parameter I added contentDetails in order to get the duration as you can see in their doc.
https://developers.google.com/youtube/v3/getting-started
Now comes the silly part. The response I get is the following:
[
{
"error": {
"code": -32602,
"message": "contentDetails",
"data": [
{
"domain": "youtube.part",
"reason": "unknownPart",
"message": "contentDetails",
"locationType": "parameter",
"location": "part"
}
]
},
"id": "gapiRpc"
}
]
"As such, the part parameter requires you to select the resource components that your application actually uses"
Thats what I did and now they dont know their own parameter anylonger?
So I was wondering how can I retrieve the duration order contentDetails in general?
best
phil
As written in the document, for search->list call, part can only take value "snippet".
Depending on the kind of the response, as a secondary call, you can do one of videos->list, playlists->list, channels->list with the id and part=snippet,contentDetails to get more details of each result item.

Complex JSON Structure

I am tackling frontend development (AngularJS) and rather than pull data from the backend (which isn't complete but renders everything to JSON), I am looking to just use hardcoded JSON.
However, I am new to this and can't seem to find anything about complex JSON structure. In a basic sense, my web app has users and the content they create. So, in my mind, there will be two databases, but I'm not sure if I'm approaching it correctly.
Users - username, location, created content, comments, etc.
"user": [
{
"userID": "12",
"userUserName": "My Username",
"userRealName": "My Real Name",
"mainInterests": [
{
"interest": "Guitar"
},
{
"interest": "Web Design"
},
{
"interest": "Hiking"
}
],
"userAbout": "All about me...",
"userComments": [
{
"comment": "this is a comment", "contentID" : "12"
},
{
"comment": "this is another comment", "contentID" : "123"
}
],
}
]
Created Content - title, description, username, comments, etc.
"mainItem": [
{
"mainID": "1",
"mainTitle": "Guitar Lessons",
"mainCreatorUserName": "My Username",
"mainCreatorRealName": "My Real Name",
"mainTags": [
{
"tag": "Intermediate"
},
{
"tag": "Acoustic"
},
{
"tag": "Guitar"
}
],
"mainAbout": "Learn guitar!",
"mainSeries": [
{
"videoFile": "file.mov",
"thumbnail": "url.jpg",
"time": "9:37",
"seriesNumber": "1",
"title": "Learn Scales"
},
{
"videoFile": "file.mov",
"thumbnail": "url.jpg",
"time": "8:12",
"seriesNumber": "2",
"title": "Learn Chords"
}
],
"userComments": [
{
"comment": "this is a comment", "userID" : "12"
},
{
"comment": "this is another comment", "userID" : "123"
}
]
}
]
And there is more complexity than that, but I just would like to understand if I'm approaching this right. Maybe I'm even approaching this entirely incorrectly (for instance, CRUD vs. REST? Does it matter here? As I understand it, REST implies that each of the objects above are resources with their own unique URI? So would JSON rendered be impacted?). I really am not sure. But ultimately, I need to use the JSON structure properly pull data into my frontend. Assumably, whatever said structure is will be mirrored and rendered in the backend.
Edit* thank you guys for the replies. I think part of my question, where I clarify "complex", is missing. So I'd like to explain. I guess more than the JSON itself, I really mean the structure of the data. For instance, in my example, I am structuring my data to all be beneath two unique objects (user and content). Is this correct? Or should I think about my data more diverse? For instance, technically I could have a comments database (where each comment is the main object). Or is that still implied in my dataset? Perhaps my question isn't even about JSON as much as it is the data structure which will happen to get rendered in JSON. Hopefully this clarifies what I mean by complex?
Any and all help is appreciated.
I'm not sure why you're making what seems to be objects into single-item arrays (as evidenced by the opening square brackets). Other than that, it looks fine to me. Generally speaking single items (like "User") are structured as an object and multiples are arrays of objects.
As for the Angular stuff, if you want to pull direct from a JSON file as a test, take a look here:
var services = angular.module('app.services', [])
services.factory('User', function($http) {
var User = function(data) {
return data;
}
User.findOne = function(id) {
return $http.get('/test_user.json').then(function(response) {
return new User(response.data);
});
};
return User;
});
I also recomment looking into Deployed for doing development without access to live data services.

Categories