Angular how to handle a nested http response - json

My angular service returns the following type of response:
{
"name": "abc",
"id": 1,
"list": [
{
"name": "listName1",
"id": 1
},
{
"name": "listName2",
"id": 2
}
]
}
This is what the call in the service looks like:
fetchX(): Observable<X> {
return this.http.get<X>(some_url)
}
Now, in my component I can access the 'name' and 'id' attribute of the returned object, but for the 'list', which is a list itself, I only get 'undefined'. I'm displaying the list elements in my application (by their name), and they don't actually show up there either (the HTML part for this is definitely correct, this isn't the issue).
myX: CustomX
myList: CustomList[]
ngOnInit() {
this.service.fetchX().subscribe((response) => {
this.myX = response,
this.myList = response.list,
console.log(response.name), //returns 'abc'
console.log(response.id), //returns '1'
console.log(response.list) //returns 'undefined'})}
The entities look like this:
export class CustomX {
name: string
id: number
list: CustomList[]
}
class CustomList {
name: string
id: number
}
What am I missing? I think I may be handling the response in the service incorrectly.

Related

Create model for JSON in Angular 9

I want to model this api response; as you see for each id we should create a property and add to object model
API response is :
{
"result": {
"id_8knimfclf": {
"text1": "X^2 is a function.",
"type": "truefalse",
"choices": ["true", "false"],
"marks": 1,
"answer": "false"
},
"id_8knimfcle": {
"text1": "Which one is true?",
"type": "multichoice",
"choices": ["first", "second", "third"],
"marks": 3,
"answer": "first"
},
....there are a lot of id due to user data enterance
}
}
I create something like this:
export interface details{
text1?string;
type?:string;
marks?:string;
choices?:string[];
answer?:string;
}
export class model{
id?:string;
detail?:details;
constructor(id:string,detail:details){
this.id=id;
this.details=detail;
}
}
but the output json file an array of object like below
[
{id:"id_8knimfclf",
details:{"text1": "X^2 is a function.","type": "truefalse","marks": 1,"choices": ["true", "false"],"answer": "false"}},
{id:"id_8knimfcle",
details:{"text1": "Which one is true","type": "multichoice","marks": 1,"choices": ["first", "second", "third"],"answer": "false"}},
//other id
]
Any help is appreciated
The problem is that when you convert an object to JSON the name of property becomes the key of the json.
If i take the example array of elements you provided and i run it in json2ts i have the following output models:
export interface Id8knimfclf {
text1: string;
type: string;
choices: string[];
marks: number;
answer: string;
}
export interface Id8knimfcle {
text1: string;
type: string;
choices: string[];
marks: number;
answer: string;
}
export interface Result {
id_8knimfclf: Id8knimfclf;
id_8knimfcle: Id8knimfcle;
}
export interface RootObject {
result: Result;
}
But i imagine it's not very useful to create a interface/model of every possible "id" of your list, also because probably you don't know which values you will have.
So we can use a trick that come from javascript and a loose object in which we can assign the name of the property dynamically instead of the value
Let's create out loose object
export interface Result {
[key: string]: details;
}
To use it to define the property name with the id values
private convertToJson(models: model[]): string {
const results: Result[] = [];
for (let model of models) {
let result: Result = {};
result[model.id] = model.details;
results.push(result);
}
return JSON.stringify(results);
}
So if you provide this input to the convertToJson function
[
{
id: "id_8knimfclf",
details: {
text1: "X^2 is a function.",
type: "truefalse",
marks: 1,
choices: ["true", "false"],
answer: "false"
}
},
{
id: "id_8knimfcle",
details: {
text1: "Which one is true",
type: "multichoice",
marks: 1,
choices: ["first", "second", "third"],
answer: "false"
}
}
]
You will have the JSON you looking for as output:
[{"id_8knimfclf":{"text1":"X^2 is a function.","type":"truefalse","marks":1,"choices":["true","false"],"answer":"false"}},{"id_8knimfcle":{"text1":"Which one is true","type":"multichoice","marks":1,"choices":["first","second","third"],"answer":"false"}}]
I have create this stackblitz example that prints the result in console.

How to use Rest Template to step into a JSON object?

I have learned how to use a rest template to access a JSON array like this:
[
{
"ID": "0ae6496f-bb0b-4ebd-a094-ca766e82f3e7",
"Confirmed": 0,
}
{
"ID": "e010ced5-c7cb-4090-a7ed-206f4c482a5b",
"Confirmed": 0,
}
]
I accessed the Confirmed for example with
public Model[] getModel() {
ResponseEntity<Model[]> response = restTemplate.getForEntity(apiUrl, Model[].class);
return response.getBody();
}
but now I have to access data in another Json from another API. The data looks like this
{
"prefixes": [
{
"region": "ap-northeast-2",
"service": "AMAZON",
},
{
"region": "eu-west-3",
"service": "AMAZON",
}
]
}
How can I access region or service inside, and what would be the proper name for this?
The first is a JSON array, the second a JSON object?
The first API is simply
https://example.com
Whereas the second is
https://example.com/data.json.
You have to create the POJO for return type:
List<RegionServiceObject> items;
Where RegionServiceObject looks:
public class RegionServiceObject {
private String region;
private String service;
// constructors, getters/setters, toString()....
}
The way of deseariliseing to object is similar to which you already wrote:
RegionServiceObject[] items = restTemplate.getForObject(url, RegionServiceObject[].class);
and access for specific item will be as usual for specific item:
for (RegionServiceObject item : items) {
item.getRegion();
item.getService();
// use them here
}

react-d3-tree error parsing external JSON

I've been trying to change inline data (which worked) to external data in a JSON file.
Originally, I had this, which worked:
const treeData = [
{
name: "Parent"
attributes: {
id: 12345678
},
children: [
{
name: "Child"
attributes: {
id: 12345679
},
}
]
}
]
return(
<Tree data = {treeData}/>
)
So now I have an external JSON file that looks like this:
{
"name": "Parent",
"attributes": {
"id": 12345678,
},
"children": [
{
"name": "Child",
"attributes": {
"id": 12345679,
}
}
]
}
]
And in my program:
const[treeData, setTreeData] = useState(undefined)
const jsonSource = '../../data/tree-data.json'
/* Used to center the tree on render */
useEffect(()=> {
// Some irrelevant code here...
// Parse JSON data (Relevant)
return function parseJSONFile(){
treeUtil.parseJSON(jsonSource).
then((data) => setTreeData({data}))
.catch((error)=>console.log(error))
}
}, []);
return(
<Tree data = {treeData} />
)
Aand it does not work. Error message:
TypeError: Cannot set property 'id' of undefined
Which makes me confused because it's my first time dealing with JSON. Any insight on this can help!
You can do it in a very simple way (No need for extra works you are doing):
import treeData from "./tree-data.json"; // Import your json file (Path to your json file)
function App() {
return <Tree data={treeData} />;
}
Your code have many problems including you write your parseJSONFile in return of useEffect, Also you just defined parseJSONFile and you are not calling it, and even you call, it will be executed on component unmount (because you call it on return of useEffect), Also initial state of your treeData is undefined and it's the cause of TypeError: Cannot set property 'id' of undefined.
My codesandbox link (https://codesandbox.io/s/react-hooks-counter-demo-dh0q4?file=/src/index.js).

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 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.