Transform JSON to an appropriate format for RESTAdapter EmberJS - json

I receive a JSON from our API that has the following format
[
{
"id": 45,
"name": "Pasta",
"_order": 0,
"is_hidden": null,
"is_list": false
},
{
"id": 46,
"name": "Salads",
"_order": 1,
"is_hidden": null,
"is_list": false
},
{
"id": 47,
"name": "Dessert",
"_order": 2,
"is_hidden": null,
"is_list": false
}
];
I see that it has invalid format for standard RESTAdapter and I need to put the name of the model first. In my example it should probably be like:
{
"category":
[
{
"id": 45,
"name": "Pasta",
"_order": 0,
"is_hidden": null,
"is_list": false
},
{
"id": 46,
"name": "Salads",
"_order": 1,
"is_hidden": null,
"is_list": false
},
{
"id": 47,
"name": "Dessert",
"_order": 2,
"is_hidden": null,
"is_list": false
}
]
}
So how to make it look this way in my adapter? It seems like I should use DS.RESTSerializer, but I can't figure out which method I should override...

I ran into this issue earlier today. A nice clean way to fix it is to define a
normalizePayload method for your ApplicationSerializer. It's made to be overwritten, so you aren't affecting anything else.
E.g.
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
return { category: payload };
}
}
If you want to do this on only some of the payloads processed then you just add a conditional inside it.
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
if (type.toString() === 'App.Category') {
return { category: payload };
}
}
}
For more info on the normalizePayload method see http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_normalizePayload

In general, you'll probably want to implement a couple of methods in your serializer if you have to tweak your JSON:
App.ApplicationSerializer = DS.RESTSerializer.extend({
normalize: function(type, hash, prop) {
// change your incoming JSON to ember-style here
},
serialize: function(record, options) {
// change your outgoing ember-data record to your server's JSON here
}
});
EDIT:
You may also in your case need to override extractArray as well: http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_extractArray

Neither normalizePayload nor normalize is working for me. What I am doing is:
// app/serializers/application.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
var payloadTemp = {}
payloadTemp[type.typeKey] = payload;
return this._super(store, type, payloadTemp);
},
extractSingle: function(store, type, payload, id) {
var payloadTemp = {}
payloadTemp[type.typeKey] = [payload];
return this._super(store, type, payloadTemp, id);
}
});

Just nest the object represented by your JSON string inside a new object which adds the new property needed and then convert back to JSON. Like this:
var yourInitialJSONString; // your JSON from API
var obj = JSON.parse(yourInitialJSONString);
var newObj = {
category: obj
};
var finalJSON = JSON.stringify(newObj);
Alternatively, though probably only best for simple case and not as universally useful as working with actual objects, you could simply concatenate to your JSON:
var yourInitialJSONString; // your JSON from API
var finalJSON = '{"category":' + yourInitialJSONString + '}';
These might prove faster for simple use cases as you avoid deserialization and serialization. I just don't like it as much as I would rather work with actual objects represented by the data. If you need to do a more complex transformation it would probably prove to be more understandable in real word terms than doing a bunch of string concatenation/manipulation.

Related

Error in JSON array comparison customization(sky-screamer)

I have below two JSONs to compare,
expected json:
[
{
"id": 1,
"code": 1,
"createdOn": null,
"lastModifiedOn": null
},
{
"id": 2,
"code": 1,
"createdOn": null,
"lastModifiedOn": null
}
]
actual json
[
{
"id": 1,
"code": 1,
"createdOn": "2019-12-31",
"lastModifiedOn": "2019-12-31",
},
{
"id": 2,
"code": 1,
"createdOn": "2019-12-31",
"lastModifiedOn": "2019-12-31",
}
]
Trying to compare by ignoring couple of nodes using below code
JSONAssert.assertEquals(actualjson, expectedjson,
new CustomComparator(JSONCompareMode.STRICT,
new Customization("createdOn", (o1, o2) -> {
return true;
}),
new Customization("lastModifiedOn", (o1, o2) -> {
return true;
})
)));
but it is failing with below assertion exception,
java.lang.AssertionError: [0].createdOn
Expected: null
got: 2019-12-31
; [0].lastModifiedOn
Expected: null
got: 2019-12-31
; [1].createdOn
Expected: null
got: 2019-12-31
; [1].lastModifiedOn
Expected: null
got: 2019-12-31
how can I compare array of json values with customization object by skipping createdon and lastmodifiedon nodes?
<groupId>org.assertj</groupId>
<version>2.2.1</version>
Yes below code snippet
JSONAssert.assertEquals(actualjson, expectedjson,
new CustomComparator(JSONCompareMode.STRICT,
new Customization("**.createdOn", (o1, o2) -> true),
new Customization("**.lastModifiedOn", (o1, o2) -> true)
)));
Eventually Customization.getCustomization method invoke appliesToPath method , which invoke this.path.matcher(path).matches();
matcher method is from java.util.regex , so if your pattern "**.createdOn" matches with path "[0].createdOn" , "[1].createdOn" then your Customisation will be added CustomComparator which inturn call your method "(o1, o2) -> true"
Approach 1: Parse the JSON and recreate the two object JSONs without the date properties.
Approach 2: Parse the JSON and put an assertion on each property to be compared.
Give a try to JsonUnit it allows you to ignore values, elements or path which should help.
Since SkyScreamer has open issue noted in github I found temporary solution and thought would be helpful for others,
https://github.com/skyscreamer/JSONassert/issues/109
solution:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
List<DomainObject> actualDomainObj = mapper.readValue(actualJson, new TypeReference<List<DomainObject>>() {
});
List<DomainObject> expectedDomainObj = mapper.readValue(expectedJson, new TypeReference<List<DomainObject>>() {
});
assertDomainObjResults(actualDomainObj.get(0), expectedDomainObj.get(0));
private void assertDomainObjResults(DomainObject actual, DomainObject expected) {
softAssertions.assertThat(actual.getId()).isEqualTo(expected.getId());
softAssertions.assertThat(actual.getLastModifiedOn()).isEqualTo(LocalDate.now());
softAssertions.assertThat(actual.getCreatedOn()).isEqualTo(LocalDate.now());
}
accept the answer if someone finds it useful.
I recently created a custom comparator that lets you use regular expressions in the 'expected' JSON:
public class ExtendedJsonComparator extends DefaultComparator {
public ExtendedJsonComparator(JSONCompareMode mode) {
super(mode);
}
#Override
public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {
String expected = expectedValue.toString().trim();
String actual = actualValue.toString();
if(expected.startsWith("${") && expected.endsWith("}")) {
String regex = expected.substring(2, expected.length() - 1);
if(!actual.matches(regex)) {
result.fail(prefix, expected, actual);
}
} else {
super.compareValues(prefix, expectedValue, actualValue, result);
}
}
}
For 'expected, you can then do the following, or create a regex to match your date format, if that's something you're testing:
[
{
"id": 1,
"code": 1,
"createdOn": "${.*}",
"lastModifiedOn": "${.*}"
},
{
"id": 2,
"code": 1,
"createdOn": "${.*}",
"lastModifiedOn": "${.*}"
}
]

Typescript - Angular http post

I am building an autocomplete functionality.
1) The backend RESTful service returns following response for partially entered keyword.
JSON response
{
"suggest": {
"resultsuggest": [
{
"text": "Ke",
"offset": 0,
"length": 2,
"options": [
{
"text": "Kevin Johnson",
"_index": "customernames",
"_type": "_doc",
"_id": "1",
"_score": 3
}]
}
]
}
}
2) In Angular application, what should I do to extract the options array from the JSON response and return it back for this fetch function??
Note - I want to use the Promise instead of Observable.
fetch(params?: HttpParams): Promise<any> {
const query = params.get('query');
const headers = new HttpHeaders().set("Content-Type", "application/json");
let postData = "{ \"_source\": \"suggest\", \"suggest\": {\"resultsuggest\" : { \"prefix\" : \""+query+"\",\"completion\" : { \"field\" : \"suggest\", \"size\" : 5 }}}}";
return this._http.post<any[]>('http://127.0.0.1:9200/customernames/_search?pretty',postData, {headers})
.pipe(map(result=> {
// what should I do to extract the Options array from the JSON response and return it back in this fetch function??
return ????;
}),
delay(400)
).toPromise();
}
Appreciate your help!
thanks!
Updated:
1) JSON response is fixed.
2) changed return this._http.post<any> to return this._http.post<any[]>
As Arcteezy suggested, the following worked
map(result=> { return result.suggest.resultsuggest[0].options; }

How to remove Task json properties in Nancy.Response.AsJson

I've made one of my API endpoints and inner logic asynchronous and when previously I've used Response.AsJson(Foo.bar()) , it would return the json representation normally, but now I see this appended to it:
{
"result": [
{
"id": "59d680cc734d1d08b4e6c89c",
"properties": {
"name": "value"
}
}
],
"id": 3,
"exception": null,
"status": 5,
"isCanceled": false,
"isCompleted": true,
"isCompletedSuccessfully": true,
"creationOptions": 0,
"asyncState": null,
"isFaulted": false
}
But I want it to be like this:
"id": "59d680cc734d1d08b4e6c89c",
"properties": {
"name": "value"
}
As I understand, it's because I've wrapped my object in a Task , but I can't figure out, how with Nancy framework, which I use the Response.AsJson, to make it so the properties are excluded. I can obviously omit the Response.AsJson of the returned object, but then response is no longer Json if requesting through web-browser for example.
For further example
NancyModule for routing API:
public ItemCatalogModule(IItemCatalog itemCatalog) : base("/itemCatalog")
{
Get("/fetch/{id}", async parameters =>
{
var id = (string) parameters.id;
var response = await Response.AsJson(itemCatalog.GetItem(id));
return response;
});
}
How the interface looks like of ItemCatalog:
public interface IItemCatalog
{
Task<Item> GetItem(string id);
}
You shoud do this :
public ItemCatalogModule(IItemCatalog itemCatalog) : base("/itemCatalog")
{
Get("/fetch/{id}", async parameters =>
{
var id = (string) parameters.id;
return Response.AsJson(await itemCatalog.GetItem(id));
});
}

Ember incorrectly deserialize json data

So I have a Spring Boot api returning a bunch of records in a list, like that:
[
{
"uid": 16587783,
"createdAt": 1391708660000,
"name": "FRaaS",
"fullName": "caarlos0/FRaaS",
"description": "Fake RT as a Service",
"homepage": "http://fraas.herokuapp.com/",
"owner": "caarlos0",
"organization": null,
"joined": false,
"private": false
},
....
more records
]
but when I try to read it in the template, I get the following error
WARNING: Encountered "0" in payload, but no model was found for model name "0" (resolved model name using chathub-ember#serializer:-rest:.modelNameFromPayloadKey("0"))
Here is my current adapter
import DS from 'ember-data';
import Ember from 'ember';
export default DS.RESTAdapter.extend({
host: 'http://127.0.0.1:8080/v1',
primaryKey: 'uid',
headers: Ember.computed('session.data.authenticated.currentUser.backendAccessToken', function() {
return {
"Auth-Token": this.get("session.data.authenticated.currentUser.backendAccessToken"),
};
})
});
what can I do in ember to allow it to read the data as I sent? I can change the API if needed, but I would rather not to
You need to use RESTSerializer. And primaryKey is for serializer not adapter. I think you use json serializer in your app.
UPDATE
Add below to your application serializer
normalizeSingleResponse(store, primaryModelClass, payload, id, requestType) {
let typeKey = primaryModelClass.modelName;
let ret = {};
ret[typeKey] = payload;
return this._normalizeResponse(store, primaryModelClass, ret, id, requestType, true);
},
normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
let pluralTypeKey = Ember.Inflector.inflector.pluralize(primaryModelClass.modelName);
let ret = {};
ret[pluralTypeKey] = payload;
return this._normalizeResponse(store, primaryModelClass, ret, id, requestType, false);
}

Ember Data One to One Relationship Record Creation Fails

I receive the following error when I use Ember Data to create records from a JSON response. What gives? I am following what the docs state.
Uncaught Error: Assertion Failed: Ember Data expected a number or string to represent the record(s) in the `user` 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 `user` property in your serializer's attrs object.
JSON being parsed:
[
{
"id": 76,
"title": "Title",
"shipped": 0,
"date": "2015-05-21T05:00:00.000Z",
"user": {
"firstName": "First Name",
"lastName": "Last Name",
"email": "hellothere#gmail.com",
"id": 1
}
}
]
Shipment Model:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
user: DS.belongsTo('user', { async: false })
});
Route:
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function() {
if(!localStorage.accessToken) {
this.transitionTo('login');
}
},
model: function() {
var shipmentObjects = [];
var App = this;
Ember.$.getJSON('http://localhost:1337/subscription/1/shipments/upcoming', function(shipments) {
shipments.forEach(function(data) {
var shipment = App.store.push('shipment', data);
shipmentObjects.pushObject(shipment);
});
});
return shipmentObjects;
}
});
You can create a custom serializer, if you can't modify your json response and manage to arrange data in other way
App.MODELNAMESerializer = DS.ActiveModelSerializer.extend({
extract: function(store, type, payload, id, requestType){
var shipments = [];
//CREATE A NEW PAYLOAD THAT EMBER CAN READ
var _payload = { };
return this._super(store, type, _payload, id, requestType);
}
});
Your json should look something like this
{
shipments: [
{
"id": 76,
"title": "Title",
"shipped": 0,
"date": "2015-05-21T05:00:00.000Z",
"user_id": 1,
}
],
"users": [
{
"firstName": "First Name",
"lastName": "Last Name",
"email": "hellothere#gmail.com",
"id": 1
}
]
}
Read the error message. It could hardly be clearer. By default, Ember Data expects an association to be represented by an ID. If the association is instead embedded, you must tell Ember Data that. You'll need something like:
// serializers/shipment.js
export default ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
user: { embedded: 'always' }
}
});
And remove the {async: false}, since the data is embedded right there.
See http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html.