I'm building a Restful API using Spring Boot and I'm running into a problem.I have an entity called Event, it has the following attributes:
Long id, String title, Calendar date, List< User > owners (OneToMany), Double price.The Entity, in turn, has the these attributes: Long id, String name, Calendar birthDate, List< Car > cars.Entity Car, in turn, has Long id and String model as attributes.
The problem is, when I access the HTTP Method GET in my Event Controller, I get this JSON:
{
"id":1,
"title":"Example event",
"date":"2017-01-01",
"owners":[
{
"id":1,
"name":"Chuck Norris",
"birthDate":"1000-05-12",
"cars":[
{
"id":1,
"model":"Shelby GT"
}
]
},
{
"id":2,
"name":"Bruce Lee",
"birthDate":"1980-05-12",
"cars":[
{
"id":2,
"model":"Ford Maverick"
}
]
}
],
"price":5
}
Instead of serializing whole owners objects, I want just their ID, like this:
{
"id":1,
"title":"Example event",
"date":"2017-01-01",
"owners":[
{
"id":1
},
{
"id":2
}
],
"price":5
}
How can I achieve this? And, is this correct to use? I think that serializing whole inner objects is a waste of resource because I don't need all their attributes at the moment, and it also causes loop with bi-directional relationships
For this use-case I developed a small jackson module for dynamic filtering:
https://github.com/Antibrumm/jackson-antpathfilter
The second possibility would be to build DTOs for your endpoints.
Add #JsonIgnore Annotation to fields that you don't want to be serialized
It sounds like you want to return a custom view of the Event, but you may actually be better off with the full object, depending on what the user of your API needs to do with the Event. One option, especially since you commented that other calls need the full User object to be serialized, is to configure the ObjectMapper used in this method with a custom serializer for User.
Related
i am sending an Array list from Producer and i am expecting to read the same arraylist at the consumer and persist into Database.
Instead of me getting the the Object i am getting and json wrapped inside the Object,which i am not able to understand why.
Below is representation of different objects.
Expexcting:
user is [Users [id=1, name=Prashantrh, nm=com.example.demo.Name#2b65d9e7]]
Pickied up at consumer side as:
[
[
{
"dmetaD":{
"id":2315,
"embedded":true,
"size":123,
"comment":"raghu",
"name":"string",
"type":"pdf",
"creationTime":"2018-05-15T20:47:48.161",
"creatorId":15001,
"creator":{
"id":15001,
"shortId":"MARC6GR",
"firstName":"V15001",
"lastName":"N15001",
"emailPref":true,
"departmentName":"RD/FNT",
"inventoryType":"P",
"langPref":"DE",
"email":"V15001.N15001#d.com"
}
},
"dCont":{
"data":"abc"
}
},
{
"dmetaD":{
"id":2316,
"embedded":true,
"size":123,
"comment":"raghu",
"name":"string",
"type":"pdf",
"creationTime":"2018-05-15T20:47:48.163",
"creatorId":15001,
"creator":{
"id":15001,
"shortId":"MARC6GR",
"firstName":"V15001",
"lastName":"N15001",
"emailPref":true,
"departmentName":"RD/FNT",
"inventoryType":"P",
"langPref":"DE",
"email":"V15001.N15001#d.com"
}
},
"dCont":{
"data":"def"
}
}
]
]
First, please provide more details as to what version of Spring Cloud Stream you are using.
That said, I am going to assume for now that you are using 2.0.0.RELEASE which means the content type of the message defaults to application/json.
So I am coming from a background of C# where I can do things in a dynamic and reflective way and I am trying to apply that to a TypeScript class I am working on writing.
Some background, I am converting an application to a web app and the backend developer doesn't want to change the backend at all to accommodate Json very well. So he is going to be sending me back Json that looks like so:
{
Columns: [
{
"ColumnName": "ClientPK",
"Label": "Client",
"DataType": "int",
"Length": 0,
"AllowNull": true,
"Format": "",
"IsReadOnly": true,
"IsDateOnly": null
}
],
Rows:[
0
]
}
I am looking to write an Angular class that extends Response that will have a special method called JsonMinimal which will understand this data and return an object for me.
import { Response } from "#angular/http";
export class ServerSource
{
SourceName: string;
MoreItems: boolean;
Error: string;
ExtendedProperties: ExtendedProperty[];
Columns: Column[];
}
export class ServerSourceResponse extends Response
{
JsonMinimal() : any
{
return null; //Something that will be a blank any type that when returned I can perform `object as FinalObject` syntax
}
}
I know StackOverflow isn't for asking for complete solutions to problems so I am only asking what is one example taking this example data and creating a dynamic response that TypeScript isn't going to yell at me for. I don't know what to do here, this developer has thousands of server-side methods and all of them return strings, in the form of a JSON or XML output. I am basically looking for a way to take his column data and combine it with the proper row data and then have a bigger object that holds a bunch of these combined object.
A usage case here after that data has been mapped to a basic object would be something like this.
Example:
var data = result.JsonMinimal() as LoginResponse; <-- Which will map to this object correctly if all the data is there in a base object.
var pk = data.ClientPK.Value;
I'm not exactly sure I understand, but you may want to try a simple approach first. Angular's http get method returns an observable that can automatically map the response to an object or an array of objects. It is also powerful enough to perform some custom mapping/transformation. You may want to look at that first.
Here is an example:
getProducts(): Observable<IProduct[]> {
return this._http.get(this._productUrl)
.map((response: Response) => <IProduct[]> response.json())
.do(data => console.log('All: ' + JSON.stringify(data)))
.catch(this.handleError);
}
Here I'm mapping a json response to an array of Product objects I've defined with an IProduct interface. Since this is just a "lambda" type function, I could add any amount of code here to transform data.
I am getting JSON returned in this format:
{
"status": "success",
"data": {
"debtor": {
"debtor_id": 1301,
"key": value,
"key": value,
"key": value
}
}
}
Somehow, my RESTAdapter needs to provide my debtor model properties from "debtor" section of the JSON.
Currently, I am getting a successful call back from the server, but a console error saying that Ember cannot find a model for "status". I can't find in the Ember Model Guide how to deal with JSON that is nested like this?
So far, I have been able to do a few simple things like extending the RESTSerializer to accept "debtor_id" as the primaryKey, and also remove the pluralization of the GET URL request... but I can't find any clear guide to reach a deeply nested JSON property.
Extending the problem detail for clarity:
I need to somehow alter the default behavior of the Adapter/Serializer, because this JSON convention is being used for many purposes other than my Ember app.
My solution thus far:
With a friend we were able to dissect the "extract API" (thanks #lame_coder for pointing me to it)
we came up with a way to extend the serializer on a case-by-case basis, but not sure if it really an "Ember Approved" solution...
// app/serializers/debtor.js
export default DS.RESTSerializer.extend({
primaryKey: "debtor_id",
extract: function(store, type, payload, id, requestType) {
payload.data.debtor.id = payload.data.debtor.debtor_id;
return payload.data.debtor;
}
});
It seems that even though I was able to change my primaryKey for requesting data, Ember was still trying to use a hard coded ID to identify the correct record (rather than the debtor_id that I had set). So we just overwrote the extract method to force Ember to look for the correct primary key that I wanted.
Again, this works for me currently, but I have yet to see if this change will cause any problems moving forward....
I would still be looking for a different solution that might be more stable/reusable/future-proof/etc, if anyone has any insights?
From description of the problem it looks like that your model definition and JSON structure is not matching. You need to make it exactly same in order to get it mapped correctly by Serializer.
If you decide to change your REST API return statement would be something like, (I am using mock data)
//your Get method on service
public object Get()
{
return new {debtor= new { debtor_id=1301,key1=value1,key2=value2}};
}
The json that ember is expecting needs to look like this:
"debtor": {
"id": 1301,
"key": value,
"key": value,
"key": value
}
It sees the status as a model that it needs to load data for. The next problem is it needs to have "id" in there and not "debtor_id".
If you need to return several objects you would do this:
"debtors": [{
"id": 1301,
"key": value,
"key": value,
"key": value
},{
"id": 1302,
"key": value,
"key": value,
"key": value
}]
Make sense?
Im using Symfony2 with FOSRestBundle on the server side, and EmberJS as client. im reciving the data from the server like this:
{
customers:[
{
id:3,
name:"Joue",
currency:{
id:5,
iso_code:"BDT"
}
}
]
}
I populate a select with a 2nd server call, where i get all the currencies.
At the moment im sending back the data (PUT - update) like this:
{
customers:[
{
id:3,
name:"Joue",
currency: 2
}
]
}
and in the controller i look up for the currency with the given id, and i save it.
$currency = $this->getDoctrine()
->getRepository('ApiBundle:Currency')
->findOneBy(array('id' => $req['customer']['currency']));
$partner->setCurrency($currency);
my question is there a way to save a request if i send it back with embedded JSON? eg:
{
customers:[
{
id:3,
name:"Joue",
currency:{
id:2,
iso_code:"XCT"
}
}
]
}
or it is fine to look up for it and handle in the controller.
My opinion:
I prefer querying the database to get the right currency (make sure nobody tries to inject some false data for instance a currency with iso_code "IAJSAIJD"). This way I'm sure everything is fine on $em::flush().
On the other hand, you could use the #ParamConverter from FOSRestBundle and send a full json. In you Customer entity you can specify a way to deserialize your currency association (See #JMSSerializer annotations, if you are using it).
class Customer
{
//...
/*
* #ORM\OneTo..
* #JMS\Type("My\Entity\Currency")
*/
$currency;
}
But this solution assumes that you allow your code to manage entities' ids, because you need to tell Doctrine that your currency already exists and this is the one with the id X. If you have a cascade persist on $currency, for instance, and you forget to $em::merge() before $em::flush(), you will end up with a new currency in your database.
That being said, I might be wrong, I'm no expert.
I'm designing REST API for many resources I need to maintain at least 2 representations of the same format (e.g. JSON) which only differ in the amount of data they expose.
For example, I have books, publishers and authors which I'm exposing via REST. So I have 3 collection of resources:books, publishers and authors.
When requesting a book I want it to include minimum information about its authors and publishers - just names, ids and hyperlinks to their full representation.
GET /books/12345
200 OK
{
id: "12345",
title: "My Book",
authors: [
{id:"111", name:"J. Smith", _href:"/authors/111"},
{id:"222", name:"J.R.R. Tolkien", _href:"/authors/222"}
],
publisher: {id:"333", name:"HarperCollins", _href:"/publishers/333"},
}
When user requests, e.g. /publishers/333 it should get more info about it, not just name and id:
{
id: "333",
name: "HarperCollins",
address : { ... },
contact : { ... },
logo : { ... }
}
In my application I have 3 classes Book, Publisher and Author, where Book contain publisher and collection of authors. And I'm using Jackson and Spring MVC to expose all the entities as JSON via REST API.
#RequestMapping("/books/{id}")
#ResponseBody
public Book getBook(String id) {
return getBookById(id);
}
#RequestMapping("/authors/{id}")
#ResponseBody
public Book getAuthor(String id) {
return getAuthorById(id);
}
Now the questions:
How would I tell the view which fields of which object should be rendered in which case?
Should I instead split each my entity into two - one for short and another for full representation (e.g. AuthorReference and Author, PublisherReference and Publisher).
Should I prefer composition or generalization if I go with option #2?
Is there any best practice how to deal with different detalizations of the same entity representation in REST applications?
Have you considered using a more standard hypermedia format, such as HAL?
Doing so, it becomes pretty clear that you would have a difference between resources, and links to other resources. For example, it might end up as:
GET /books/123
200 OK
{
"id": "123"
"title": "My Book"
"_links": {
"author": [
{ "href": "/authors/111", "title": "J. Smith" },
{ "href": "/authors/321", "title": "Jules Verne" }
],
"publisher": { "href": "/publishers/123", "title": "Harper Collins" }
}
}
In terms of implementation, I would say you can either:
Create custom DTO classes for each resource representation, along the lines of:
public class BookDto {
#JsonProperty(value="_links")
public LinkCollection getLinks() { }
public String getTitle() { }
}
Use the same classes, but use filters to modify the objects before Jackson serializes, and use some combination of #JsonProperty/#JsonIgnore.
1/ According to Spring MVC documentation, to serialize your object over HTTP you have to define your own class implementing HttpMessageConverter<T> with T either Book, Publisher or Author.
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/remoting.html#rest-message-conversion
2/ It is not necessary to split your objects in two. The logic to select what fields you pick up from an Author is contained in the AuthorToHttp implement HttpMessageConverter<Author>. Just need to develop a AuthorReferenceToHttp implement HttpMessageConverter<Author> to use when you want less data to be send over HTTP.
Another solution could be either AuthorReference extend Author or Author extend AuthorReference on one hand and AuthorToHttp implement HttpMessageConverter<Author> with AuthorReferenceToHttp implement HttpMessageConverter<AuthorReference> on the other hand to serialize your Author.
3/ Inheritance/Generalization is enough. Not necessarily a need for a pattern.
Composite is a wrong choice, you don't need a tree structure. According to circumstances you want either all data about an author - default behavior -, or the main data about an author - "proxied" behavior -. Proxy would be a better choice.
4/ No idea.